# edoobox API v2 Öffentliche edoobox API v2 mit fachlichen Endpunktbeschreibungen, quellgeprüften Parametern, Request- und Antwortschemas. Authentifizierung erfolgt mit JWT beziehungsweise bestehender Sitzung und dem ausgewählten Kontokontext. Objektverweise verwenden öffentliche v2-IDs. Mandantenabhängige Felder, Berechtigungen und fachliche Voraussetzungen sind bei der jeweiligen Operation beschrieben. Beispiele sind synthetisch und enthalten keine produktiven Zugangsdaten. OpenAPI 3.2.1; API-Version 2. 893 Operationen, 619 Pfade, 74 Gruppen. 0 Operationen sind ausdrücklich teilweise dokumentiert. 540 sind ausdrücklich zustandsändernd; weitere können unbekannte Seiteneffekte haben. ## Dateien - Vollständige OpenAPI-Datei: /docs/api/v2/openapi.json - LLM-Einstieg: /docs/api/v2/llms.txt - Gesamte Referenz als Markdown: /docs/api/v2/llms-full.txt - SHA-256 der unveränderten Quelldatei: d949f74b23f0b92449733090417006200cfa67329887bc2a8cc4de152b5a6124 ## Server aus der Spezifikation ```json [ { "url": "https://app2.edoobox.com", "description": "edoobox API" } ] ``` Eine Entwicklungsadresse ist keine bestätigte Produktionsadresse. Setze API_ORIGIN für deine Instanz. Bei Servervariablen sind die dokumentierten Werte einzusetzen. ## Authentifizierungsdefinitionen ```json { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Existing edoobox v2 access token. Never substitute API key/secret headers." }, "sessionCookie": { "type": "apiKey", "in": "cookie", "name": "_edlogin", "description": "Existing browser login session, with the same permission checks as Bearer authentication." }, "accountContext": { "type": "apiKey", "in": "header", "name": "edid", "description": "Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account." }, "trainerToken": { "type": "http", "scheme": "bearer", "description": "Dedicated revocable trainer-course token; not a normal account JWT." } } ``` Security Requirements verwenden ODER zwischen den Einträgen und UND zwischen den Verfahren eines Eintrags. Eine Operation kann globale Vorgaben überschreiben; ihre eigene Definition ist massgeblich. ## Lesen und Aufrufen Die Referenz wird aus der bereitgestellten Datei erzeugt. Beschreibungen, Beispiele, Schemas und x-edoobox-Hinweise sind Quelldaten. Beispiele begründen keine zusätzlichen Berechtigungen und ergänzen keine unbeschriebenen Felder. Mutationen und unbekannte Seiteneffekte niemals allein anhand von GET/POST klassifizieren. Antwortstatus sowie error/success und dokumentierte Voraussetzungen prüfen. ## Endpunkte ### accounting - [POST /v2/accounting/bexio/authorize](/docs/api/v2/operations/post-v2-accounting-bexio-authorize-0d5dbcf2856f.md): Bexio-Verbindung autorisieren - [GET /v2/accounting/bexio/callback](/docs/api/v2/operations/get-v2-accounting-bexio-callback-5ff04154c57c.md): Bexio-Browserrückkehr verarbeiten - [POST /v2/accounting/bexio/callback](/docs/api/v2/operations/post-v2-accounting-bexio-callback-bd1d0ead32b5.md): Bexio-Autorisierung abschließen - [GET /v2/accounting/connections](/docs/api/v2/operations/get-v2-accounting-connections-35a813f0250f.md): Buchhaltungsverbindungen des Kontos anzeigen - [GET /v2/accounting/connections/{connectionId}](/docs/api/v2/operations/get-v2-accounting-connections-connectionid-18a150b02235.md): Buchhaltungsverbindung prüfen - [PUT /v2/accounting/connections/{connectionId}](/docs/api/v2/operations/put-v2-accounting-connections-connectionid-70a9a4a151f8.md): Buchhaltungseinstellungen speichern - [DELETE /v2/accounting/connections/{connectionId}](/docs/api/v2/operations/delete-v2-accounting-connections-connectionid-9088e03453ed.md): Buchhaltungsverbindung trennen - [GET /v2/accounting/connections/{connectionId}/issues](/docs/api/v2/operations/get-v2-accounting-connections-connectionid-issues-17d9d5f00c86.md): Buchhaltungsprobleme prüfen - [POST /v2/accounting/connections/{connectionId}/issues/reconcile](/docs/api/v2/operations/post-v2-accounting-connections-connectionid-issues-reconcile-7527a7b278ed.md): Buchhaltungsproblem erneut abgleichen - [GET /v2/accounting/connections/{connectionId}/mappings](/docs/api/v2/operations/get-v2-accounting-connections-connectionid-mappings-23e5500a6826.md): Buchhaltungszuordnungen bearbeiten - [PUT /v2/accounting/connections/{connectionId}/mappings](/docs/api/v2/operations/put-v2-accounting-connections-connectionid-mappings-03e163ea003b.md): Buchhaltungszuordnungen speichern - [GET /v2/accounting/connections/{connectionId}/mappings/taxes](/docs/api/v2/operations/get-v2-accounting-connections-connectionid-mappings-taxes-aee634d4465c.md): Aktive Bexio-Steuern abrufen - [GET /v2/accounting/connections/{connectionId}/sync](/docs/api/v2/operations/get-v2-accounting-connections-connectionid-sync-5ed8fbf01893.md): Synchronisationsverlauf prüfen - [POST /v2/accounting/connections/{connectionId}/sync](/docs/api/v2/operations/post-v2-accounting-connections-connectionid-sync-26f7bc909c01.md): Zahlungsabgleich starten - [GET /v2/accounting/providers](/docs/api/v2/operations/get-v2-accounting-providers-a7cad4e5f265.md): Verfügbare Buchhaltungsanbieter anzeigen ### admin - [GET /v2/admin](/docs/api/v2/operations/get-v2-admin-929fb6897432.md): Administratorprofile abrufen - [PUT /v2/admin](/docs/api/v2/operations/put-v2-admin-eedad1b72f7a.md): Administratorprofil aktualisieren - [POST /v2/admin](/docs/api/v2/operations/post-v2-admin-3bd3b687be3b.md): Administrator anlegen oder einladen - [DELETE /v2/admin](/docs/api/v2/operations/delete-v2-admin-283a5b07f571.md): Administrator löschen - [GET /v2/admin/list](/docs/api/v2/operations/get-v2-admin-list-ac87ba46a850.md): Administratorprofile abrufen - [GET /v2/admin/list/course-creation-links](/docs/api/v2/operations/get-v2-admin-list-course-creation-links-118b06150833.md): Kurs-Erstellungslinks aller Trainer abrufen - [POST /v2/admin/list/customized_data](/docs/api/v2/operations/post-v2-admin-list-customized-data-bf7f39a8586f.md): Kundenspezifischen Administrator-Export starten - [POST /v2/admin/list/files](/docs/api/v2/operations/post-v2-admin-list-files-b221ae1eefd8.md): Datei zu einem Administrator hochladen - [GET /v2/admin/list/files/list](/docs/api/v2/operations/get-v2-admin-list-files-list-a7ae1d3d219e.md): Dateimetadaten zu Administratoren abrufen - [GET /v2/admin/list/files/{fileId}/download](/docs/api/v2/operations/get-v2-admin-list-files-fileid-download-53117622f756.md): Avatar- oder Administratordatei herunterladen - [GET /v2/admin/list/workload](/docs/api/v2/operations/get-v2-admin-list-workload-7baae5d00fb3.md): Auslastung eines Administrators abrufen - [DELETE /v2/admin/remove/files/{id}](/docs/api/v2/operations/delete-v2-admin-remove-files-id-7de18088c36f.md): Administratordatei entfernen - [POST /v2/admin/task/customized_data](/docs/api/v2/operations/post-v2-admin-task-customized-data-91eaf4a76bd8.md): Kundenspezifischen Administrator-Export starten - [PUT /v2/admin/update/files/{id}](/docs/api/v2/operations/put-v2-admin-update-files-id-c2e056f23007.md): Metadaten einer Administratordatei ändern - [POST /v2/admin/upload/files](/docs/api/v2/operations/post-v2-admin-upload-files-88005c68370d.md): Datei zu einem Administrator hochladen - [GET /v2/admin/{id}](/docs/api/v2/operations/get-v2-admin-id-b0497eccc41c.md): Administratorprofile abrufen - [PUT /v2/admin/{id}](/docs/api/v2/operations/put-v2-admin-id-c7a854574876.md): Administratorprofil aktualisieren - [DELETE /v2/admin/{id}](/docs/api/v2/operations/delete-v2-admin-id-4bb8bdc480f1.md): Administrator löschen - [GET /v2/admin/{id}/course-creation-link](/docs/api/v2/operations/get-v2-admin-id-course-creation-link-0ad3f71a05f6.md): Kurs-Erstellungslink eines Trainers abrufen - [POST /v2/admin/{id}/course-creation-link](/docs/api/v2/operations/post-v2-admin-id-course-creation-link-6b6f0b0f4d48.md): Kurs-Erstellungslink eines Trainers erneuern - [GET /v2/admin/{id}/dashboard](/docs/api/v2/operations/get-v2-admin-id-dashboard-b50cd1f026a6.md): Administratorübersicht abrufen - [GET /v2/admin/{id}/files](/docs/api/v2/operations/get-v2-admin-id-files-3c73d5378a8e.md): Dateimetadaten zu Administratoren abrufen - [PUT /v2/admin/{id}/files](/docs/api/v2/operations/put-v2-admin-id-files-5b31e0380755.md): Metadaten einer Administratordatei ändern - [DELETE /v2/admin/{id}/files](/docs/api/v2/operations/delete-v2-admin-id-files-7855817b37a6.md): Administratordatei entfernen - [DELETE /v2/admin/{id}/identity-provider](/docs/api/v2/operations/delete-v2-admin-id-identity-provider-8700e33abb1e.md): SAML-Anmeldung eines Administrators lösen - [POST /v2/admin/{id}/invitation](/docs/api/v2/operations/post-v2-admin-id-invitation-ce892c322616.md): Administratoreinladung erneut senden - [GET /v2/admin/{id}/workload](/docs/api/v2/operations/get-v2-admin-id-workload-e362decfe986.md): Auslastung eines Administrators abrufen ### adminconfig - [GET /v2/adminconfig](/docs/api/v2/operations/get-v2-adminconfig-73e949bd1087.md): Persönliche Administratorkonfigurationen abrufen - [PUT /v2/adminconfig](/docs/api/v2/operations/put-v2-adminconfig-69f3b463d5d9.md): Persönliche Administratorkonfiguration ändern - [POST /v2/adminconfig](/docs/api/v2/operations/post-v2-adminconfig-c79b7c9614b5.md): Persönliche Administratorkonfiguration anlegen - [DELETE /v2/adminconfig](/docs/api/v2/operations/delete-v2-adminconfig-81d7d14d9292.md): Persönliche Administratorkonfiguration löschen - [GET /v2/adminconfig/list](/docs/api/v2/operations/get-v2-adminconfig-list-54bf79c1068b.md): Persönliche Administratorkonfigurationen abrufen - [GET /v2/adminconfig/list/LUT](/docs/api/v2/operations/get-v2-adminconfig-list-lut-59c385abdd19.md): Konfigurationsvorlagen für Administratoren abrufen - [GET /v2/adminconfig/{id}](/docs/api/v2/operations/get-v2-adminconfig-id-6df63f162547.md): Persönliche Administratorkonfigurationen abrufen - [PUT /v2/adminconfig/{id}](/docs/api/v2/operations/put-v2-adminconfig-id-6f8770904dea.md): Persönliche Administratorkonfiguration ändern - [DELETE /v2/adminconfig/{id}](/docs/api/v2/operations/delete-v2-adminconfig-id-2aa90481f8d5.md): Persönliche Administratorkonfiguration löschen - [GET /v2/adminconfig/{id}/LUT](/docs/api/v2/operations/get-v2-adminconfig-id-lut-871cf36a8b2a.md): Konfigurationsvorlagen für Administratoren abrufen ### admission - [GET /v2/admission/admin](/docs/api/v2/operations/get-v2-admission-admin-512b30ac2a01.md): Angemeldeten Leiter und Präsenzkonfiguration laden - [PUT /v2/admission/admissioncontrol/{code}](/docs/api/v2/operations/put-v2-admission-admissioncontrol-code-0b4443beb227.md): Einlassprüfung im Leiterkontext durchführen - [POST /v2/admission/attendance](/docs/api/v2/operations/post-v2-admission-attendance-09de15417f07.md): Präsenz im Leiterkontext erfassen - [PUT /v2/admission/attendance/{attendanceId}](/docs/api/v2/operations/put-v2-admission-attendance-attendanceid-fc6eecd3f4f7.md): Präsenz im Leiterkontext ändern - [GET /v2/admission/attendance_view](/docs/api/v2/operations/get-v2-admission-attendance-view-1557971a3d19.md): Teilnehmer und Präsenz eines Leitertermins laden - [POST /v2/admission/ical_feed](/docs/api/v2/operations/post-v2-admission-ical-feed-c9bb44a43c81.md): Persönlichen iCalendar-Link erzeugen - [GET /v2/admission/internal_documents](/docs/api/v2/operations/get-v2-admission-internal-documents-a4502123b82b.md): Interne Angebotsdokumente im Leiterkontext lesen - [POST /v2/admission/internal_documents](/docs/api/v2/operations/post-v2-admission-internal-documents-7dfb24bc9790.md): Internes Dokument zu einem Leitertermin hochladen - [GET /v2/admission/internal_documents/files/{fileId}/download](/docs/api/v2/operations/get-v2-admission-internal-documents-files-fileid-download-af46da4c1ab3.md): Internes Angebotsdokument herunterladen - [GET /v2/admission/next_dates](/docs/api/v2/operations/get-v2-admission-next-dates-64a0a1e1f055.md): Nächste zugewiesene Termine laden - [POST /v2/admission/pusherwss](/docs/api/v2/operations/post-v2-admission-pusherwss-364480ea35b0.md): Echtzeitkanal für die Einlassansicht autorisieren ### admissioncontrol - [PUT /v2/admissioncontrol](/docs/api/v2/operations/put-v2-admissioncontrol-e742f10cb7c7.md): Barcode prüfen und Präsenz setzen - [PUT /v2/admissioncontrol/{code}](/docs/api/v2/operations/put-v2-admissioncontrol-code-3c08dffda9a6.md): Barcode prüfen und Präsenz setzen ### ai - [GET /v2/ai](/docs/api/v2/operations/get-v2-ai-12b2eb011b7f.md): KI-Textvorlagen oder Provider-Verbindung prüfen - [POST /v2/ai](/docs/api/v2/operations/post-v2-ai-5df399c43154.md): Text mit KI-Vorlage erzeugen, optimieren oder übersetzen - [POST /v2/ai/list/demo_pusherevent](/docs/api/v2/operations/post-v2-ai-list-demo-pusherevent-6e035fdd7b28.md): Abschlussereignis für KI-Übersetzung senden - [POST /v2/ai/list/offertranslate](/docs/api/v2/operations/post-v2-ai-list-offertranslate-ff9aef43c75c.md): Angebot in die aktiven Kontosprachen übersetzen - [POST /v2/ai/translate/offertranslate](/docs/api/v2/operations/post-v2-ai-translate-offertranslate-b93871a0376e.md): Angebot in die aktiven Kontosprachen übersetzen ### aiassistant - [GET /v2/aiassistant](/docs/api/v2/operations/get-v2-aiassistant-83e778394f1a.md): KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen - [GET /v2/aiassistant/artifacts/{artifactId}](/docs/api/v2/operations/get-v2-aiassistant-artifacts-artifactid-d2801fa34644.md): KI-Artefakt prüfen oder geschützt herunterladen - [GET /v2/aiassistant/auth-context](/docs/api/v2/operations/get-v2-aiassistant-auth-context-7eb523dd8b22.md): Aktuellen Berechtigungs- und Revisionskontext lesen - [GET /v2/aiassistant/config](/docs/api/v2/operations/get-v2-aiassistant-config-435f6efd01b4.md): KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen - [PUT /v2/aiassistant/config](/docs/api/v2/operations/put-v2-aiassistant-config-fde2efdb91f3.md): KI-Assistent konfigurieren - [GET /v2/aiassistant/conversations](/docs/api/v2/operations/get-v2-aiassistant-conversations-ff2b60e59eaf.md): Eigene KI-Gespräche auflisten - [POST /v2/aiassistant/conversations](/docs/api/v2/operations/post-v2-aiassistant-conversations-1af1d4d92f35.md): KI-Gespräch beginnen - [GET /v2/aiassistant/conversations/{conversationId}](/docs/api/v2/operations/get-v2-aiassistant-conversations-conversationid-a662604c5c8a.md): KI-Gespräch mit Nachrichten und Läufen lesen - [DELETE /v2/aiassistant/conversations/{conversationId}](/docs/api/v2/operations/delete-v2-aiassistant-conversations-conversationid-619afa29f057.md): Eigenes KI-Gespräch löschen - [POST /v2/aiassistant/conversations/{conversationId}/messages](/docs/api/v2/operations/post-v2-aiassistant-conversations-conversationid-messages-f7cd33582dcf.md): KI-Nachricht senden und Entwurf zur Prüfung anfordern - [GET /v2/aiassistant/packages](/docs/api/v2/operations/get-v2-aiassistant-packages-992ab2c37937.md): Verfügbare KI-Tokenpakete lesen - [POST /v2/aiassistant/purchases](/docs/api/v2/operations/post-v2-aiassistant-purchases-24e2a1bc4480.md): KI-Tokenpaket gegen Rechnung bestellen - [GET /v2/aiassistant/purchases/{id}](/docs/api/v2/operations/get-v2-aiassistant-purchases-id-9cb9c61f8f81.md): Tokenkauf und Zahlungsabgleich abrufen - [GET /v2/aiassistant/runs/{runId}](/docs/api/v2/operations/get-v2-aiassistant-runs-runid-a91d8fa49830.md): KI-Laufstatus und geprüften Änderungsvorschlag lesen - [POST /v2/aiassistant/runs/{runId}/approve](/docs/api/v2/operations/post-v2-aiassistant-runs-runid-approve-b0ac3dd7b395.md): Geprüften KI-Änderungsvorschlag ausdrücklich freigeben - [POST /v2/aiassistant/runs/{runId}/cancel](/docs/api/v2/operations/post-v2-aiassistant-runs-runid-cancel-209adfe84c73.md): KI-Lauf abbrechen - [POST /v2/aiassistant/runs/{runId}/retry](/docs/api/v2/operations/post-v2-aiassistant-runs-runid-retry-215203d2c480.md): Fehlgeschlagenen KI-Entwurf mit neuer Anfrage wiederholen - [GET /v2/aiassistant/status](/docs/api/v2/operations/get-v2-aiassistant-status-46e5778eecd2.md): KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen ### apiaccess - [GET /v2/apiaccess](/docs/api/v2/operations/get-v2-apiaccess-7fe54754648f.md): API-Zugänge auflisten - [PUT /v2/apiaccess](/docs/api/v2/operations/put-v2-apiaccess-121dcf58cca9.md): API-Zugang ändern - [POST /v2/apiaccess](/docs/api/v2/operations/post-v2-apiaccess-d47d75f646b6.md): API-Zugang anlegen - [DELETE /v2/apiaccess](/docs/api/v2/operations/delete-v2-apiaccess-3718b625bfcb.md): API-Zugang entfernen - [GET /v2/apiaccess/list](/docs/api/v2/operations/get-v2-apiaccess-list-b6f996338af6.md): API-Zugänge auflisten - [GET /v2/apiaccess/{id}](/docs/api/v2/operations/get-v2-apiaccess-id-cf301681dce4.md): API-Zugang lesen - [PUT /v2/apiaccess/{id}](/docs/api/v2/operations/put-v2-apiaccess-id-29c3a1422a50.md): API-Zugang ändern - [DELETE /v2/apiaccess/{id}](/docs/api/v2/operations/delete-v2-apiaccess-id-75ee806380b3.md): API-Zugang entfernen ### attendance - [GET /v2/attendance](/docs/api/v2/operations/get-v2-attendance-ff7f57fd2b6f.md): Präsenzstatus lesen - [PUT /v2/attendance](/docs/api/v2/operations/put-v2-attendance-7c78ae2413bf.md): Vorhandenen Präsenzstatus ändern - [POST /v2/attendance](/docs/api/v2/operations/post-v2-attendance-6bd600c04cd3.md): Präsenzstatus für einen Teilnehmer erfassen - [DELETE /v2/attendance](/docs/api/v2/operations/delete-v2-attendance-9865fea909a0.md): Präsenzdatensatz löschen - [POST /v2/attendance/bulk/bulk_attendance_date](/docs/api/v2/operations/post-v2-attendance-bulk-bulk-attendance-date-b5584e6c3df7.md): Präsenz für alle Teilnehmer eines Termins setzen - [GET /v2/attendance/list](/docs/api/v2/operations/get-v2-attendance-list-73d5d49ab11a.md): Präsenzstatus lesen - [POST /v2/attendance/list/I_want_demo_data](/docs/api/v2/operations/post-v2-attendance-list-i-want-demo-data-c29262cb40e8.md): Demo-Präsenz für Entwicklung erzeugen - [POST /v2/attendance/list/bulk_attendance_date](/docs/api/v2/operations/post-v2-attendance-list-bulk-attendance-date-ad948a8bd2d4.md): Präsenz für alle Teilnehmer eines Termins setzen - [POST /v2/attendance/list/bulk_attendance_offer](/docs/api/v2/operations/post-v2-attendance-list-bulk-attendance-offer-250fbb31776e.md): Präsenz für alle Termine eines Angebots setzen - [GET /v2/attendance/list/log](/docs/api/v2/operations/get-v2-attendance-list-log-21ee2f289e20.md): Änderungsverlauf der Präsenz lesen - [GET /v2/attendance/list/table](/docs/api/v2/operations/get-v2-attendance-list-table-0a73d5c5d712.md): Teilnehmer-Präsenzmatrix laden - [GET /v2/attendance/{id}](/docs/api/v2/operations/get-v2-attendance-id-5036f3f67389.md): Präsenzstatus lesen - [PUT /v2/attendance/{id}](/docs/api/v2/operations/put-v2-attendance-id-c7d93ab04d31.md): Vorhandenen Präsenzstatus ändern - [DELETE /v2/attendance/{id}](/docs/api/v2/operations/delete-v2-attendance-id-0f7776b28a83.md): Präsenzdatensatz löschen ### auth - [GET /v2/auth](/docs/api/v2/operations/get-v2-auth-7c13933ce678.md): Login-Sitzungen prüfen - [POST /v2/auth](/docs/api/v2/operations/post-v2-auth-307d710f787a.md): Administrator anmelden oder API-Token beziehen - [DELETE /v2/auth](/docs/api/v2/operations/delete-v2-auth-8afe31840763.md): Login-Sitzung sperren und entfernen - [GET /v2/auth/DEMO_LOGIN](/docs/api/v2/operations/get-v2-auth-demo-login-377c1d527f6f.md): Beispielansicht für den App-Login anzeigen - [POST /v2/auth/DEMO_LOGIN](/docs/api/v2/operations/post-v2-auth-demo-login-98b3971176a1.md): Beispielansicht für den App-Login anzeigen - [GET /v2/auth/access_token](/docs/api/v2/operations/get-v2-auth-access-token-693552a44a18.md): App-Anmeldecode gegen Access-Token tauschen - [POST /v2/auth/access_token](/docs/api/v2/operations/post-v2-auth-access-token-c346cfd271c6.md): App-Anmeldecode gegen Access-Token tauschen - [GET /v2/auth/admin_invitation](/docs/api/v2/operations/get-v2-auth-admin-invitation-3d9e8ca18467.md): Gültigkeit einer Administrator-Einladung prüfen - [POST /v2/auth/admin_invitation/accept](/docs/api/v2/operations/post-v2-auth-admin-invitation-accept-855634b85639.md): Administrator-Einladung annehmen - [GET /v2/auth/customer_endpoint/SpracheCB_screen](/docs/api/v2/operations/get-v2-auth-customer-endpoint-sprachecb-screen-b16b9df141ab.md): Kundenspezifische Demoanzeige öffnen - [POST /v2/auth/customer_endpoint/SpracheCB_screen](/docs/api/v2/operations/post-v2-auth-customer-endpoint-sprachecb-screen-fc3dc46282e9.md): Kundenspezifische Demoanzeige öffnen - [GET /v2/auth/customer_endpoint/appwork_dates](/docs/api/v2/operations/get-v2-auth-customer-endpoint-appwork-dates-b0776183f6f1.md): Termine für die kundenspezifische Anzeige lesen - [POST /v2/auth/customer_endpoint/appwork_dates](/docs/api/v2/operations/post-v2-auth-customer-endpoint-appwork-dates-5653de862cd9.md): Termine für die kundenspezifische Anzeige lesen - [GET /v2/auth/customer_endpoint/appwork_screen](/docs/api/v2/operations/get-v2-auth-customer-endpoint-appwork-screen-446cf6243121.md): Kundenspezifische Bildschirmanzeige öffnen - [POST /v2/auth/customer_endpoint/appwork_screen](/docs/api/v2/operations/post-v2-auth-customer-endpoint-appwork-screen-33bba12f8f51.md): Kundenspezifische Bildschirmanzeige öffnen - [GET /v2/auth/endpoint/date_calendar/{token}](/docs/api/v2/operations/get-v2-auth-endpoint-date-calendar-token-d68e236eadfc.md): Freigegebenen Widget-Kalender lesen - [POST /v2/auth/endpoint/date_calendar/{token}](/docs/api/v2/operations/post-v2-auth-endpoint-date-calendar-token-72137829794a.md): Freigegebenen Widget-Kalender lesen - [GET /v2/auth/forgot_password/change_request](/docs/api/v2/operations/get-v2-auth-forgot-password-change-request-0a07c73d06f9.md): Passwort-Rücksetzlink prüfen - [POST /v2/auth/forgot_password/change_request](/docs/api/v2/operations/post-v2-auth-forgot-password-change-request-b4b002069949.md): Neues Passwort über Rücksetzlink speichern - [GET /v2/auth/forgot_password/send_request](/docs/api/v2/operations/get-v2-auth-forgot-password-send-request-7b7dcfb619b7.md): Passwort-Zurücksetzung anfordern - [POST /v2/auth/forgot_password/send_request](/docs/api/v2/operations/post-v2-auth-forgot-password-send-request-4e22b58b845c.md): Passwort-Zurücksetzung anfordern - [GET /v2/auth/i/{token}](/docs/api/v2/operations/get-v2-auth-i-token-fd7107030e79.md): Freigegebenen iCalendar-Feed abrufen - [POST /v2/auth/i/{token}](/docs/api/v2/operations/post-v2-auth-i-token-a82364f7c7d2.md): Freigegebenen iCalendar-Feed abrufen - [GET /v2/auth/ical/{token}](/docs/api/v2/operations/get-v2-auth-ical-token-120416f5337e.md): Freigegebenen iCalendar-Feed abrufen - [POST /v2/auth/ical/{token}](/docs/api/v2/operations/post-v2-auth-ical-token-5f779e87466c.md): Freigegebenen iCalendar-Feed abrufen - [GET /v2/auth/list](/docs/api/v2/operations/get-v2-auth-list-801e13cdf180.md): Login-Sitzungen prüfen - [PUT /v2/auth/list/2fa](/docs/api/v2/operations/put-v2-auth-list-2fa-707d00e70636.md): Zweiten Faktor für die Anmeldung bestätigen - [PUT /v2/auth/list/logout](/docs/api/v2/operations/put-v2-auth-list-logout-1cfd1957e9cc.md): Alle Login-Kontexte abmelden - [PUT /v2/auth/list/logout_edid](/docs/api/v2/operations/put-v2-auth-list-logout-edid-a3de692f8831.md): Einzelnen Login-Kontext abmelden - [PUT /v2/auth/list/refresh](/docs/api/v2/operations/put-v2-auth-list-refresh-d288e58f3f2f.md): Login-Sitzung verlängern - [PUT /v2/auth/list/skip_counter](/docs/api/v2/operations/put-v2-auth-list-skip-counter-ae9bd6c155fc.md): Zahlungshinweis einmal überspringen - [GET /v2/auth/oauth2/microsoft/logout](/docs/api/v2/operations/get-v2-auth-oauth2-microsoft-logout-2c196f165b2a.md): Leeren microsoft-Logout-Callback aufrufen - [POST /v2/auth/oauth2/microsoft/logout](/docs/api/v2/operations/post-v2-auth-oauth2-microsoft-logout-b536abfc3f45.md): Leeren microsoft-Logout-Callback aufrufen - [GET /v2/auth/oauth2/microsoft/redirect](/docs/api/v2/operations/get-v2-auth-oauth2-microsoft-redirect-924eaa7e829a.md): Microsoft-Mailkonto-Verbindung abschließen - [POST /v2/auth/oauth2/microsoft/redirect](/docs/api/v2/operations/post-v2-auth-oauth2-microsoft-redirect-33a99ee4e60e.md): Microsoft-Mailkonto-Verbindung abschließen - [GET /v2/auth/oauth2/microsoft_calendar/logout](/docs/api/v2/operations/get-v2-auth-oauth2-microsoft-calendar-logout-3d762ecfc4d9.md): Leeren microsoft_calendar-Logout-Callback aufrufen - [POST /v2/auth/oauth2/microsoft_calendar/logout](/docs/api/v2/operations/post-v2-auth-oauth2-microsoft-calendar-logout-3c504d6887bd.md): Leeren microsoft_calendar-Logout-Callback aufrufen - [GET /v2/auth/oauth2/microsoft_calendar/redirect](/docs/api/v2/operations/get-v2-auth-oauth2-microsoft-calendar-redirect-a0e2c7ba7e21.md): Microsoft-Supportkalender-Verbindung abschließen - [POST /v2/auth/oauth2/microsoft_calendar/redirect](/docs/api/v2/operations/post-v2-auth-oauth2-microsoft-calendar-redirect-6023a44efbea.md): Microsoft-Supportkalender-Verbindung abschließen - [GET /v2/auth/oauth2/paypal](/docs/api/v2/operations/get-v2-auth-oauth2-paypal-d12c2488d293.md): PayPal-Händlerverbindung abschließen - [POST /v2/auth/oauth2/paypal](/docs/api/v2/operations/post-v2-auth-oauth2-paypal-cd044d9b8bc4.md): PayPal-Händlerverbindung abschließen - [GET /v2/auth/oauth2/paypal_bridge](/docs/api/v2/operations/get-v2-auth-oauth2-paypal-bridge-f1bde4de77a0.md): PayPal-Händlerverbindung für die Legacy-Ansicht abschließen - [POST /v2/auth/oauth2/paypal_bridge](/docs/api/v2/operations/post-v2-auth-oauth2-paypal-bridge-d5cf7793db01.md): PayPal-Händlerverbindung für die Legacy-Ansicht abschließen - [GET /v2/auth/oauth2/stripe](/docs/api/v2/operations/get-v2-auth-oauth2-stripe-55d25cde83ea.md): Stripe-Connect-Autorisierung abschließen - [POST /v2/auth/oauth2/stripe](/docs/api/v2/operations/post-v2-auth-oauth2-stripe-5aa3564f16f6.md): Stripe-Connect-Autorisierung abschließen - [GET /v2/auth/payment/stripe](/docs/api/v2/operations/get-v2-auth-payment-stripe-83ffa38c5f71.md): Stripe-Zahlungsrückkehr für Kontoguthaben verarbeiten - [POST /v2/auth/payment/stripe](/docs/api/v2/operations/post-v2-auth-payment-stripe-0b77a4bfe245.md): Stripe-Zahlungsrückkehr für Kontoguthaben verarbeiten - [GET /v2/auth/saml/logout](/docs/api/v2/operations/get-v2-auth-saml-logout-8c92a1022ebe.md): SAML-Einzelabmeldung verarbeiten - [POST /v2/auth/saml/logout](/docs/api/v2/operations/post-v2-auth-saml-logout-6b6028292017.md): SAML-Einzelabmeldung verarbeiten - [GET /v2/auth/saml/redirect/{providerId}/{accountId}](/docs/api/v2/operations/get-v2-auth-saml-redirect-providerid-accountid-c9c6211ec77c.md): SAML-Anmeldung beim Identitätsanbieter starten - [POST /v2/auth/saml/redirect/{providerId}/{accountId}](/docs/api/v2/operations/post-v2-auth-saml-redirect-providerid-accountid-92bd5582652b.md): SAML-Anmeldung beim Identitätsanbieter starten - [POST /v2/auth/saml/reply](/docs/api/v2/operations/post-v2-auth-saml-reply-f7b34370e447.md): SAML-Identitätsantwort empfangen - [GET /v2/auth/saml/sso/{accountIdentifier}](/docs/api/v2/operations/get-v2-auth-saml-sso-accountidentifier-4131d8bd7155.md): SAML-Anmeldeanbieter eines Kontos auflisten - [POST /v2/auth/saml/sso/{accountIdentifier}](/docs/api/v2/operations/post-v2-auth-saml-sso-accountidentifier-113c9252c086.md): SAML-Anmeldeanbieter eines Kontos auflisten - [PUT /v2/auth/skip/skip_counter](/docs/api/v2/operations/put-v2-auth-skip-skip-counter-e7dd42046222.md): Zahlungshinweis einmal überspringen - [POST /v2/auth/system-feedback-reports](/docs/api/v2/operations/post-v2-auth-system-feedback-reports-ce5db1f1c196.md): Öffentlichen Systemfehlerbericht speichern - [GET /v2/auth/webhook/paypal](/docs/api/v2/operations/get-v2-auth-webhook-paypal-c3d16760c59a.md): Signierte PayPal-Ereignisse verarbeiten - [POST /v2/auth/webhook/paypal](/docs/api/v2/operations/post-v2-auth-webhook-paypal-d7d9fb23bd07.md): Signierte PayPal-Ereignisse verarbeiten - [GET /v2/auth/webhook/pingen/{token}](/docs/api/v2/operations/get-v2-auth-webhook-pingen-token-a8ff0a3ee761.md): Pingen-Briefstatus und Kosten übernehmen - [POST /v2/auth/webhook/pingen/{token}](/docs/api/v2/operations/post-v2-auth-webhook-pingen-token-ffc061155585.md): Pingen-Briefstatus und Kosten übernehmen - [GET /v2/auth/{id}](/docs/api/v2/operations/get-v2-auth-id-a64f955dde76.md): Login-Sitzungen prüfen - [DELETE /v2/auth/{id}](/docs/api/v2/operations/delete-v2-auth-id-f5f3b41aaf33.md): Login-Sitzung sperren und entfernen - [PUT /v2/auth/{id}/2fa](/docs/api/v2/operations/put-v2-auth-id-2fa-8c1e61d4f995.md): Zweiten Faktor für die Anmeldung bestätigen - [PUT /v2/auth/{id}/logout](/docs/api/v2/operations/put-v2-auth-id-logout-65b94ea621ab.md): Alle Login-Kontexte abmelden - [PUT /v2/auth/{id}/logout_edid](/docs/api/v2/operations/put-v2-auth-id-logout-edid-8785fd418c70.md): Einzelnen Login-Kontext abmelden - [PUT /v2/auth/{id}/refresh](/docs/api/v2/operations/put-v2-auth-id-refresh-131d403b7f1b.md): Login-Sitzung verlängern ### auth2fa - [GET /v2/auth2fa](/docs/api/v2/operations/get-v2-auth2fa-24717591861c.md): Zweite Faktoren auflisten - [POST /v2/auth2fa](/docs/api/v2/operations/post-v2-auth2fa-f6c45c4faf6f.md): Zweiten Faktor in zwei Schritten einrichten - [GET /v2/auth2fa/list](/docs/api/v2/operations/get-v2-auth2fa-list-10fb6813e09c.md): Zweite Faktoren auflisten - [GET /v2/auth2fa/list/backupkey](/docs/api/v2/operations/get-v2-auth2fa-list-backupkey-554b29ef5b38.md): Metadaten der Wiederherstellungscodes lesen - [POST /v2/auth2fa/list/backupkey](/docs/api/v2/operations/post-v2-auth2fa-list-backupkey-db711fcef063.md): Zehn neue Wiederherstellungscodes erzeugen - [GET /v2/auth2fa/list/passkey](/docs/api/v2/operations/get-v2-auth2fa-list-passkey-1fd7654b8b6e.md): Eigene Passkeys auflisten - [POST /v2/auth2fa/list/passkey](/docs/api/v2/operations/post-v2-auth2fa-list-passkey-6faf1a71d7d3.md): Browser-Passkey registrieren - [POST /v2/auth2fa/list/passkey-options](/docs/api/v2/operations/post-v2-auth2fa-list-passkey-options-7b35b9910aea.md): Browser-Challenge zur Passkey-Registrierung starten - [GET /v2/auth2fa/{id}](/docs/api/v2/operations/get-v2-auth2fa-id-d2d98456bade.md): Zweite Faktoren lesen - [DELETE /v2/auth2fa/{id}](/docs/api/v2/operations/delete-v2-auth2fa-id-dab15c8961f4.md): Zweiten Faktor deaktivieren - [DELETE /v2/auth2fa/{id}/passkey](/docs/api/v2/operations/delete-v2-auth2fa-id-passkey-befe083719db.md): Eigenen Passkey entfernen ### automationrule - [GET /v2/automationrule](/docs/api/v2/operations/get-v2-automationrule-04b9bbece2c0.md): Automationsregeln suchen - [PUT /v2/automationrule](/docs/api/v2/operations/put-v2-automationrule-fb57ec8ada79.md): Automationsregel bearbeiten - [POST /v2/automationrule](/docs/api/v2/operations/post-v2-automationrule-c38368da0dd4.md): Automationsregel erstellen - [DELETE /v2/automationrule](/docs/api/v2/operations/delete-v2-automationrule-9f24f78ed5e7.md): Automationsregel entfernen - [GET /v2/automationrule/catalog](/docs/api/v2/operations/get-v2-automationrule-catalog-ce6249144c0a.md): Automationsmöglichkeiten und Kontoreferenzen lesen - [GET /v2/automationrule/executions](/docs/api/v2/operations/get-v2-automationrule-executions-25dcad288835.md): Automationsausführungen und Aktionsstatus lesen - [GET /v2/automationrule/executions/{executionId}](/docs/api/v2/operations/get-v2-automationrule-executions-executionid-f3a7ca63234c.md): Einzelne Automationsausführung untersuchen - [GET /v2/automationrule/samples](/docs/api/v2/operations/get-v2-automationrule-samples-6fa5b46cff2f.md): Kontoobjekte für eine Regelsimulation suchen - [POST /v2/automationrule/validate](/docs/api/v2/operations/post-v2-automationrule-validate-8d13f7467400.md): Ungespeicherte Automationsdefinition prüfen - [GET /v2/automationrule/{id}](/docs/api/v2/operations/get-v2-automationrule-id-35ecd8934ba0.md): Automationsregel im Detail lesen - [PUT /v2/automationrule/{id}](/docs/api/v2/operations/put-v2-automationrule-id-63ed4230bed5.md): Automationsregel bearbeiten - [DELETE /v2/automationrule/{id}](/docs/api/v2/operations/delete-v2-automationrule-id-679cfbb05ecc.md): Automationsregel entfernen - [POST /v2/automationrule/{id}/activate](/docs/api/v2/operations/post-v2-automationrule-id-activate-6265e7c84c73.md): Automationsregel aktivieren - [POST /v2/automationrule/{id}/simulate](/docs/api/v2/operations/post-v2-automationrule-id-simulate-3acb87f03ce1.md): Automationsregel an einem Kontoobjekt simulieren - [POST /v2/automationrule/{id}/test](/docs/api/v2/operations/post-v2-automationrule-id-test-08a9d15ef411.md): Automationsregel an einem Kontoobjekt simulieren - [POST /v2/automationrule/{id}/validate](/docs/api/v2/operations/post-v2-automationrule-id-validate-d3ca2ea763a0.md): Gespeicherte Automationsregel prüfen ### avataradmin - [POST /v2/avataradmin](/docs/api/v2/operations/post-v2-avataradmin-9885df813842.md): Administratoravatar hochladen oder ersetzen - [DELETE /v2/avataradmin](/docs/api/v2/operations/delete-v2-avataradmin-cedc6443937e.md): Administratoravatar entfernen - [GET /v2/avataradmin/list/files/{fileId}/download](/docs/api/v2/operations/get-v2-avataradmin-list-files-fileid-download-4b127620db04.md): Avatar- oder Administratordatei herunterladen ### avataruser - [POST /v2/avataruser](/docs/api/v2/operations/post-v2-avataruser-5c76305bebbc.md): Teilnehmeravatar hochladen oder ersetzen - [DELETE /v2/avataruser](/docs/api/v2/operations/delete-v2-avataruser-23d4682cfd1d.md): Teilnehmeravatar entfernen - [GET /v2/avataruser/list/files/{fileId}/download](/docs/api/v2/operations/get-v2-avataruser-list-files-fileid-download-ef897c3fa81b.md): Teilnehmeravatar herunterladen ### barcode - [GET /v2/barcode](/docs/api/v2/operations/get-v2-barcode-fb143b2c9ae0.md): Teilnehmer-Barcode auflisten - [PUT /v2/barcode](/docs/api/v2/operations/put-v2-barcode-f7ce21241a56.md): Teilnehmer-Barcode ändern - [POST /v2/barcode](/docs/api/v2/operations/post-v2-barcode-cd60a4822e54.md): Teilnehmer-Barcode erstellen - [GET /v2/barcode/list](/docs/api/v2/operations/get-v2-barcode-list-df9c53f1af55.md): Teilnehmer-Barcode auflisten - [GET /v2/barcode/{id}](/docs/api/v2/operations/get-v2-barcode-id-f145b1cab85c.md): Teilnehmer-Barcode lesen - [PUT /v2/barcode/{id}](/docs/api/v2/operations/put-v2-barcode-id-f39f18ebfcf7.md): Teilnehmer-Barcode ändern ### booking - [GET /v2/booking](/docs/api/v2/operations/get-v2-booking-92084f0c9a6f.md): Buchungen und Buchungsstatus lesen - [PUT /v2/booking](/docs/api/v2/operations/put-v2-booking-8b2174b0e115.md): Interne Referenz einer Buchung ändern - [POST /v2/booking](/docs/api/v2/operations/post-v2-booking-8bed8fe40cb6.md): Buchung für einen bestehenden Kunden erstellen - [GET /v2/booking/list](/docs/api/v2/operations/get-v2-booking-list-98e44cb72be3.md): Buchungen und Buchungsstatus lesen - [GET /v2/booking/list/accountconnect](/docs/api/v2/operations/get-v2-booking-list-accountconnect-39eb0347949f.md): Buchungen für Account Connect exportieren - [GET /v2/booking/list/data](/docs/api/v2/operations/get-v2-booking-list-data-58949bf0c846.md): Vollständige Buchungsübersicht lesen - [GET /v2/booking/list/labelexport](/docs/api/v2/operations/get-v2-booking-list-labelexport-9e22f0458912.md): Buchungsreferenzen exportieren - [GET /v2/booking/list/send/booking_confirmation](/docs/api/v2/operations/get-v2-booking-list-send-booking-confirmation-8aef3554973e.md): Buchungsbestätigung per E-Mail senden - [GET /v2/booking/list/send/booking_reminder](/docs/api/v2/operations/get-v2-booking-list-send-booking-reminder-6fb00eb2bb59.md): Buchungserinnerung per E-Mail senden - [GET /v2/booking/list/send/payment_reminder](/docs/api/v2/operations/get-v2-booking-list-send-payment-reminder-fcc6082d122b.md): Zahlungserinnerung per E-Mail senden - [GET /v2/booking/list/send/visit_confirmation](/docs/api/v2/operations/get-v2-booking-list-send-visit-confirmation-a168824a8a63.md): Teilnahmebestätigungen per E-Mail senden - [GET /v2/booking/{id}](/docs/api/v2/operations/get-v2-booking-id-3ff9b7d1c5a2.md): Buchungen und Buchungsstatus lesen - [PUT /v2/booking/{id}](/docs/api/v2/operations/put-v2-booking-id-da0e82cdb7e7.md): Interne Referenz einer Buchung ändern - [PUT /v2/booking/{id}/add_participation](/docs/api/v2/operations/put-v2-booking-id-add-participation-5c348bca800a.md): Wartelistenbuchung als Teilnahme übernehmen - [PUT /v2/booking/{id}/ask_participation](/docs/api/v2/operations/put-v2-booking-id-ask-participation-e48b40daa93d.md): Teilnahmeangebot per E-Mail versenden - [GET /v2/booking/{id}/booking.pdf](/docs/api/v2/operations/get-v2-booking-id-booking-pdf-bf1ce27daf36.md): Buchungsbestätigung als PDF laden - [GET /v2/booking/{id}/booking.pdf/{cacheToken}](/docs/api/v2/operations/get-v2-booking-id-booking-pdf-cachetoken-af5a65517175.md): Buchungsbestätigung als PDF laden - [PUT /v2/booking/{id}/cancel](/docs/api/v2/operations/put-v2-booking-id-cancel-cc0b0bda3f27.md): Gesamte Buchung stornieren - [PUT /v2/booking/{id}/cancellation_condition](/docs/api/v2/operations/put-v2-booking-id-cancellation-condition-733b3cc51f07.md): Stornierungsbedingungen einer Buchung ändern - [PUT /v2/booking/{id}/cancellationcondition](/docs/api/v2/operations/put-v2-booking-id-cancellationcondition-98654429b854.md): Stornierungsbedingungen einer Buchung ändern - [PUT /v2/booking/{id}/cancellationcondition/remove](/docs/api/v2/operations/put-v2-booking-id-cancellationcondition-remove-6ce13477317e.md): Stornierungsbedingungen einer Buchung ändern - [GET /v2/booking/{id}/certificate.pdf/{participantOrUserId}](/docs/api/v2/operations/get-v2-booking-id-certificate-pdf-participantoruserid-92dba7eba8c1.md): Teilnahmezertifikat einer Teilnehmerposition laden - [GET /v2/booking/{id}/certificate.pdf/{participantOrUserId}/{cacheToken}](/docs/api/v2/operations/get-v2-booking-id-certificate-pdf-participantoruserid-cachetoken-af736f948b75.md): Teilnahmezertifikat einer Teilnehmerposition laden - [GET /v2/booking/{id}/dashboard](/docs/api/v2/operations/get-v2-booking-id-dashboard-5da3d9bb0aeb.md): Daten für das Buchungsdashboard laden - [GET /v2/booking/{id}/data](/docs/api/v2/operations/get-v2-booking-id-data-5aa3c5786d34.md): Vollständige Buchungsübersicht lesen - [PUT /v2/booking/{id}/dates](/docs/api/v2/operations/put-v2-booking-id-dates-c28c9f45f0c6.md): Gebuchte Lektionstermine vollständig ersetzen - [PUT /v2/booking/{id}/deliverables](/docs/api/v2/operations/put-v2-booking-id-deliverables-e3171f217026.md): Menge einer gebuchten Zusatzleistung ändern - [PUT /v2/booking/{id}/invoice_address](/docs/api/v2/operations/put-v2-booking-id-invoice-address-3327863cc56d.md): Abweichende Rechnungsadresse ändern - [GET /v2/booking/{id}/invoice_address_suggestions](/docs/api/v2/operations/get-v2-booking-id-invoice-address-suggestions-014c1eb79efc.md): Frühere Rechnungsadressen vorschlagen - [PUT /v2/booking/{id}/membership_benefit](/docs/api/v2/operations/put-v2-booking-id-membership-benefit-813ec71205e3.md): Angewendeten Mitgliedschaftsrabatt entfernen - [PUT /v2/booking/{id}/multi_offer](/docs/api/v2/operations/put-v2-booking-id-multi-offer-23d7abac36c1.md): Unterangebot einer Teilnehmerposition ändern - [PUT /v2/booking/{id}/pricecategory](/docs/api/v2/operations/put-v2-booking-id-pricecategory-a45d3cd46903.md): Preiskategorie einer Teilnehmerposition wechseln - [PUT /v2/booking/{id}/promotion](/docs/api/v2/operations/put-v2-booking-id-promotion-1392eecf4efd.md): Promotionscode einer Buchung setzen oder entfernen - [PUT /v2/booking/{id}/question](/docs/api/v2/operations/put-v2-booking-id-question-7fad627db28f.md): Antwort einer Buchung bearbeiten - [PUT /v2/booking/{id}/rebooking](/docs/api/v2/operations/put-v2-booking-id-rebooking-5dcd983f5f95.md): Buchung in ein anderes Angebot umbuchen oder kopieren - [GET /v2/booking/{id}/send/booking_confirmation](/docs/api/v2/operations/get-v2-booking-id-send-booking-confirmation-b424217fea9b.md): Buchungsbestätigung per E-Mail senden - [GET /v2/booking/{id}/send/booking_reminder](/docs/api/v2/operations/get-v2-booking-id-send-booking-reminder-bf1c06381019.md): Buchungserinnerung per E-Mail senden - [GET /v2/booking/{id}/send/payment_reminder](/docs/api/v2/operations/get-v2-booking-id-send-payment-reminder-cb742ccee403.md): Zahlungserinnerung per E-Mail senden - [GET /v2/booking/{id}/send/visit_confirmation](/docs/api/v2/operations/get-v2-booking-id-send-visit-confirmation-e70a8c2c46cb.md): Teilnahmebestätigungen per E-Mail senden - [GET /v2/booking/{id}/ticket.pdf](/docs/api/v2/operations/get-v2-booking-id-ticket-pdf-e8f8ffba3db7.md): Tickets einer Buchung als PDF laden - [GET /v2/booking/{id}/ticket.pdf/{cacheToken}](/docs/api/v2/operations/get-v2-booking-id-ticket-pdf-cachetoken-2f366b30178f.md): Tickets einer Buchung als PDF laden - [PUT /v2/booking/{id}/user_add](/docs/api/v2/operations/put-v2-booking-id-user-add-c09f5f5a48a4.md): Benannten Teilnehmer oder unbenannte Plätze hinzufügen - [PUT /v2/booking/{id}/user_status](/docs/api/v2/operations/put-v2-booking-id-user-status-536f48bcc009.md): Teilnehmerposition aktivieren oder abmelden ### bookingqueue - [GET /v2/bookingqueue](/docs/api/v2/operations/get-v2-bookingqueue-0d06f1118ef4.md): Status eines Warteraum-Eintrags prüfen - [PUT /v2/bookingqueue](/docs/api/v2/operations/put-v2-bookingqueue-aacb426db65d.md): Warteraum-Eintrag am Leben halten - [DELETE /v2/bookingqueue](/docs/api/v2/operations/delete-v2-bookingqueue-38e24c0185f0.md): Warteraum-Platz freigeben - [POST /v2/bookingqueue/list/heartbeat](/docs/api/v2/operations/post-v2-bookingqueue-list-heartbeat-066663f3ec18.md): Warteraum-Eintrag am Leben halten - [POST /v2/bookingqueue/list/join](/docs/api/v2/operations/post-v2-bookingqueue-list-join-9312afcc89cb.md): Buchungs-Warteraum betreten - [POST /v2/bookingqueue/list/release](/docs/api/v2/operations/post-v2-bookingqueue-list-release-8784b47f5351.md): Warteraum-Platz freigeben - [POST /v2/bookingqueue/list/status](/docs/api/v2/operations/post-v2-bookingqueue-list-status-24ee3fc45555.md): Status eines Warteraum-Eintrags prüfen ### bootstrap - [GET /v2/bootstrap](/docs/api/v2/operations/get-v2-bootstrap-972e7b8cf2de.md): Bootstrap-Konfiguration auflisten - [PUT /v2/bootstrap](/docs/api/v2/operations/put-v2-bootstrap-f47edac9bbfd.md): Bootstrap-Konfiguration ändern - [POST /v2/bootstrap](/docs/api/v2/operations/post-v2-bootstrap-639f33224b60.md): Bootstrap-Konfiguration anlegen - [GET /v2/bootstrap/list](/docs/api/v2/operations/get-v2-bootstrap-list-e9a114b8607f.md): Bootstrap-Konfiguration auflisten - [GET /v2/bootstrap/list/cdn](/docs/api/v2/operations/get-v2-bootstrap-list-cdn-b00ad3a64e3a.md): Design und CSS für ein Angebot oder eine Kategorie bestimmen - [GET /v2/bootstrap/{id}](/docs/api/v2/operations/get-v2-bootstrap-id-0b44550ed9ec.md): Bootstrap-Konfiguration lesen - [PUT /v2/bootstrap/{id}](/docs/api/v2/operations/put-v2-bootstrap-id-fe697f659f86.md): Bootstrap-Konfiguration ändern - [PUT /v2/bootstrap/{id}/compiling_scss](/docs/api/v2/operations/put-v2-bootstrap-id-compiling-scss-3166b34ecf96.md): Bootstrap-5.3-CSS kompilieren - [GET /v2/bootstrap/{id}/wp-css](/docs/api/v2/operations/get-v2-bootstrap-id-wp-css-e5a0dfae3859.md): WordPress-CSS erzeugen und CDN-Adresse beziehen ### cancellationcondition - [GET /v2/cancellationcondition](/docs/api/v2/operations/get-v2-cancellationcondition-5edbe705ded3.md): Stornierungsgruppen und Widerrufsregeln abrufen - [PUT /v2/cancellationcondition](/docs/api/v2/operations/put-v2-cancellationcondition-9a8c8f1050e6.md): Stornierungsgruppe und Regelwerk ändern - [POST /v2/cancellationcondition](/docs/api/v2/operations/post-v2-cancellationcondition-454977a43840.md): Stornierungsgruppe und Gebührenregeln anlegen - [DELETE /v2/cancellationcondition](/docs/api/v2/operations/delete-v2-cancellationcondition-c6a1f355d04e.md): Stornierungsgruppe entfernen - [GET /v2/cancellationcondition/list](/docs/api/v2/operations/get-v2-cancellationcondition-list-0d137c0e9b20.md): Stornierungsgruppen und Widerrufsregeln abrufen - [GET /v2/cancellationcondition/{id}](/docs/api/v2/operations/get-v2-cancellationcondition-id-e6acbe0fcfff.md): Stornierungsgruppen und Widerrufsregeln abrufen - [PUT /v2/cancellationcondition/{id}](/docs/api/v2/operations/put-v2-cancellationcondition-id-a053810f2068.md): Stornierungsgruppe und Regelwerk ändern - [DELETE /v2/cancellationcondition/{id}](/docs/api/v2/operations/delete-v2-cancellationcondition-id-7836d9135ad5.md): Stornierungsgruppe entfernen ### category - [GET /v2/category](/docs/api/v2/operations/get-v2-category-775ac7a79858.md): Kategorien auflisten - [PUT /v2/category](/docs/api/v2/operations/put-v2-category-7d12f46ada34.md): Kategorie aktualisieren - [POST /v2/category](/docs/api/v2/operations/post-v2-category-dfa13e5a7716.md): Kategorie anlegen - [GET /v2/category/list](/docs/api/v2/operations/get-v2-category-list-08041d608522.md): Kategorien auflisten - [GET /v2/category/list/shareurl](/docs/api/v2/operations/get-v2-category-list-shareurl-7ba47ced201a.md): Freigabe- und Buchungslinks für Kategorie abrufen - [GET /v2/category/{id}](/docs/api/v2/operations/get-v2-category-id-5e1e737e2ff2.md): Kategorie abrufen - [PUT /v2/category/{id}](/docs/api/v2/operations/put-v2-category-id-ee60b183feeb.md): Kategorie aktualisieren - [DELETE /v2/category/{id}](/docs/api/v2/operations/delete-v2-category-id-79070e9cbdc7.md): Kategorie löschen - [PUT /v2/category/{id}/auto_delete](/docs/api/v2/operations/put-v2-category-id-auto-delete-72461efc808f.md): Kategorie in den Papierkorb verschieben - [GET /v2/category/{id}/dashboard](/docs/api/v2/operations/get-v2-category-id-dashboard-bb81c88ea00d.md): Kategorieübersicht mit Buchungsstatistik abrufen - [DELETE /v2/category/{id}/empty](/docs/api/v2/operations/delete-v2-category-id-empty-4dfdfdd3d18e.md): Papierkorb leeren - [PUT /v2/category/{id}/image](/docs/api/v2/operations/put-v2-category-id-image-8ee5afa5960f.md): Kategorie mit einem Bild versehen - [PUT /v2/category/{id}/move](/docs/api/v2/operations/put-v2-category-id-move-7d7891371b71.md): Kategorie im Baum verschieben - [GET /v2/category/{id}/shareurl](/docs/api/v2/operations/get-v2-category-id-shareurl-426a1d58910d.md): Freigabe- und Buchungslinks für Kategorie abrufen ### charts - [GET /v2/charts/list/donut/offer](/docs/api/v2/operations/get-v2-charts-list-donut-offer-6ce6201318a5.md): Meistgebuchte Angebote mit Impressionen lesen - [GET /v2/charts/list/donut/user](/docs/api/v2/operations/get-v2-charts-list-donut-user-333545b36b67.md): Aktive Teilnehmer nach Sprache zählen - [GET /v2/charts/list/line/offer](/docs/api/v2/operations/get-v2-charts-list-line-offer-a2f535bace3d.md): Angebotskennzahlen im Zeitverlauf vergleichen - [GET /v2/charts/list/status/booking](/docs/api/v2/operations/get-v2-charts-list-status-booking-74629c387726.md): Buchungen nach Abschlussstatus zählen - [GET /v2/charts/list/timeline/booking](/docs/api/v2/operations/get-v2-charts-list-timeline-booking-acdbc44eb079.md): Begonnene Buchungen im Zeitverlauf lesen - [GET /v2/charts/list/timeline/email](/docs/api/v2/operations/get-v2-charts-list-timeline-email-2100a3019f33.md): Fehlerfreie E-Mails im Zeitverlauf zählen - [GET /v2/charts/list/timeline/transaction](/docs/api/v2/operations/get-v2-charts-list-timeline-transaction-bb74613b8ddf.md): Buchungsumsätze nach Währung im Zeitverlauf lesen - [GET /v2/charts/list/timeline/user](/docs/api/v2/operations/get-v2-charts-list-timeline-user-c1cf7169d226.md): Buchende Teilnehmer im Zeitverlauf zählen ### country - [GET /v2/country](/docs/api/v2/operations/get-v2-country-8ead7db170e8.md): Land auflisten - [GET /v2/country/list](/docs/api/v2/operations/get-v2-country-list-9580fe8db777.md): Land auflisten - [GET /v2/country/{id}](/docs/api/v2/operations/get-v2-country-id-361064458b50.md): Land lesen ### datastructure - [GET /v2/datastructure](/docs/api/v2/operations/get-v2-datastructure-c54f592bc7a4.md): Teilnehmer-Datenfelder und Reihenfolge lesen - [PUT /v2/datastructure](/docs/api/v2/operations/put-v2-datastructure-a04f4244e602.md): Teilnehmer-Datenfeld oder Reihenfolge ändern - [PUT /v2/datastructure/{key}](/docs/api/v2/operations/put-v2-datastructure-key-d33a3109e664.md): Teilnehmer-Datenfeld oder Reihenfolge ändern ### date - [GET /v2/date](/docs/api/v2/operations/get-v2-date-0bf37ca457eb.md): Termine auflisten - [PUT /v2/date](/docs/api/v2/operations/put-v2-date-ad3b2fdf7012.md): Termin aktualisieren - [POST /v2/date](/docs/api/v2/operations/post-v2-date-0b4faa838f42.md): Termin anlegen - [DELETE /v2/date](/docs/api/v2/operations/delete-v2-date-4302ebcc70bd.md): Termin löschen - [POST /v2/date/feed/ical_feed](/docs/api/v2/operations/post-v2-date-feed-ical-feed-008028acd1be.md): URL für einen iCalendar-Feed erzeugen - [GET /v2/date/list](/docs/api/v2/operations/get-v2-date-list-8ed1ba9721ab.md): Termine auflisten - [POST /v2/date/list/ical_feed](/docs/api/v2/operations/post-v2-date-list-ical-feed-41c609c55337.md): URL für einen iCalendar-Feed erzeugen - [GET /v2/date/{id}](/docs/api/v2/operations/get-v2-date-id-b8fa5f28b3bf.md): Termin abrufen - [PUT /v2/date/{id}](/docs/api/v2/operations/put-v2-date-id-0e171d7851d8.md): Termin aktualisieren - [DELETE /v2/date/{id}](/docs/api/v2/operations/delete-v2-date-id-902d1db52cb0.md): Termin löschen - [GET /v2/date/{id}/dashboard](/docs/api/v2/operations/get-v2-date-id-dashboard-2c16f748ea29.md): Terminübersicht mit Belegungszahlen abrufen ### deliverables - [GET /v2/deliverables](/docs/api/v2/operations/get-v2-deliverables-663bca705147.md): Zusatzleistung auflisten - [PUT /v2/deliverables](/docs/api/v2/operations/put-v2-deliverables-b4ab23725c68.md): Zusatzleistung ändern - [POST /v2/deliverables](/docs/api/v2/operations/post-v2-deliverables-338b697ad73c.md): Zusatzleistung erstellen - [GET /v2/deliverables/list](/docs/api/v2/operations/get-v2-deliverables-list-e0325125d0de.md): Zusatzleistung auflisten - [GET /v2/deliverables/{id}](/docs/api/v2/operations/get-v2-deliverables-id-19cd2bb2abff.md): Zusatzleistung lesen - [PUT /v2/deliverables/{id}](/docs/api/v2/operations/put-v2-deliverables-id-c4fa8b6e2598.md): Zusatzleistung ändern ### deliverablesgroup - [GET /v2/deliverablesgroup](/docs/api/v2/operations/get-v2-deliverablesgroup-ac3373fa7bc9.md): Leistungsgruppe auflisten - [PUT /v2/deliverablesgroup](/docs/api/v2/operations/put-v2-deliverablesgroup-2c6e98ad9168.md): Leistungsgruppe ändern - [POST /v2/deliverablesgroup](/docs/api/v2/operations/post-v2-deliverablesgroup-4cb21c97c63a.md): Leistungsgruppe erstellen - [DELETE /v2/deliverablesgroup](/docs/api/v2/operations/delete-v2-deliverablesgroup-d28e5c938dbd.md): Leistungsgruppe löschen - [GET /v2/deliverablesgroup/list](/docs/api/v2/operations/get-v2-deliverablesgroup-list-1824045fd959.md): Leistungsgruppe auflisten - [GET /v2/deliverablesgroup/{id}](/docs/api/v2/operations/get-v2-deliverablesgroup-id-2e53b3d2d2d4.md): Leistungsgruppe lesen - [PUT /v2/deliverablesgroup/{id}](/docs/api/v2/operations/put-v2-deliverablesgroup-id-1adada061350.md): Leistungsgruppe ändern - [DELETE /v2/deliverablesgroup/{id}](/docs/api/v2/operations/delete-v2-deliverablesgroup-id-c1f89998d211.md): Leistungsgruppe löschen ### design - [GET /v2/design](/docs/api/v2/operations/get-v2-design-3621d6572671.md): Buchungsdesign auflisten - [PUT /v2/design](/docs/api/v2/operations/put-v2-design-4f42b1fede22.md): Buchungsdesign ändern - [POST /v2/design](/docs/api/v2/operations/post-v2-design-40bba1ac0873.md): Buchungsdesign anlegen - [DELETE /v2/design](/docs/api/v2/operations/delete-v2-design-5069f86832c6.md): Buchungsdesign löschen - [GET /v2/design/list](/docs/api/v2/operations/get-v2-design-list-45f38fa34a7d.md): Buchungsdesign auflisten - [GET /v2/design/list/LUT](/docs/api/v2/operations/get-v2-design-list-lut-4d60373a44eb.md): Öffentliches Vorlage-Design zum Kopieren lesen - [GET /v2/design/list/templates](/docs/api/v2/operations/get-v2-design-list-templates-bae7e4caf5fb.md): Verfügbare Seitenvorlagen eines Buchungsdesigns auflisten - [GET /v2/design/{id}](/docs/api/v2/operations/get-v2-design-id-dd748e1775cc.md): Buchungsdesign lesen - [PUT /v2/design/{id}](/docs/api/v2/operations/put-v2-design-id-ea63094c5abc.md): Buchungsdesign ändern - [DELETE /v2/design/{id}](/docs/api/v2/operations/delete-v2-design-id-dbbbce0abf1e.md): Buchungsdesign löschen ### dinaco - [GET /v2/dinaco](/docs/api/v2/operations/get-v2-dinaco-49c203285d43.md): Dinaco-Veranstaltung auflisten - [PUT /v2/dinaco](/docs/api/v2/operations/put-v2-dinaco-26ca936aadf6.md): Dinaco-Veranstaltung ändern - [POST /v2/dinaco](/docs/api/v2/operations/post-v2-dinaco-1f5f71f01fd4.md): Dinaco-Veranstaltung erstellen - [DELETE /v2/dinaco](/docs/api/v2/operations/delete-v2-dinaco-bf322d4f112d.md): Dinaco-Veranstaltung löschen - [POST /v2/dinaco/event/permission](/docs/api/v2/operations/post-v2-dinaco-event-permission-58ce0469c2f1.md): Dinaco-Angebotsberechtigung erstellen - [POST /v2/dinaco/event/section](/docs/api/v2/operations/post-v2-dinaco-event-section-f553a9dbd6f6.md): Dinaco-Zutrittsbereich erstellen - [GET /v2/dinaco/list](/docs/api/v2/operations/get-v2-dinaco-list-8c713481e327.md): Dinaco-Veranstaltung auflisten - [GET /v2/dinaco/list/permission](/docs/api/v2/operations/get-v2-dinaco-list-permission-53161eb5984e.md): Dinaco-Angebotsberechtigung auflisten - [POST /v2/dinaco/list/permission](/docs/api/v2/operations/post-v2-dinaco-list-permission-5ac9210cb6d0.md): Dinaco-Angebotsberechtigung erstellen - [DELETE /v2/dinaco/list/permission](/docs/api/v2/operations/delete-v2-dinaco-list-permission-c75317071af3.md): Dinaco-Angebotsberechtigung löschen - [GET /v2/dinaco/list/section](/docs/api/v2/operations/get-v2-dinaco-list-section-2a3dd634c89b.md): Dinaco-Zutrittsbereich auflisten - [PUT /v2/dinaco/list/section](/docs/api/v2/operations/put-v2-dinaco-list-section-692b8094da9c.md): Dinaco-Zutrittsbereich ändern - [POST /v2/dinaco/list/section](/docs/api/v2/operations/post-v2-dinaco-list-section-a1c7e4036a8a.md): Dinaco-Zutrittsbereich erstellen - [DELETE /v2/dinaco/list/section](/docs/api/v2/operations/delete-v2-dinaco-list-section-e4ab629782ca.md): Dinaco-Zutrittsbereich löschen - [GET /v2/dinaco/{id}](/docs/api/v2/operations/get-v2-dinaco-id-9b6df1be0791.md): Dinaco-Veranstaltung lesen - [PUT /v2/dinaco/{id}](/docs/api/v2/operations/put-v2-dinaco-id-f9a023bede6d.md): Dinaco-Veranstaltung ändern - [DELETE /v2/dinaco/{id}](/docs/api/v2/operations/delete-v2-dinaco-id-e22490ddb6c9.md): Dinaco-Veranstaltung löschen - [DELETE /v2/dinaco/{id}/permission](/docs/api/v2/operations/delete-v2-dinaco-id-permission-1e7e54cc2dd6.md): Dinaco-Angebotsberechtigung löschen - [PUT /v2/dinaco/{id}/section](/docs/api/v2/operations/put-v2-dinaco-id-section-bdae86a1cb10.md): Dinaco-Zutrittsbereich ändern - [DELETE /v2/dinaco/{id}/section](/docs/api/v2/operations/delete-v2-dinaco-id-section-2fa34e9728be.md): Dinaco-Zutrittsbereich löschen - [PUT /v2/dinaco/{id}/sync](/docs/api/v2/operations/put-v2-dinaco-id-sync-4c7ff9ee5eca.md): Dinaco: Teilnehmertickets synchronisieren - [PUT /v2/dinaco/{id}/syncSection](/docs/api/v2/operations/put-v2-dinaco-id-syncsection-85d1a3796a2f.md): Dinaco: Zutrittsbereiche und Berechtigungen synchronisieren - [PUT /v2/dinaco/{id}/syncTicket](/docs/api/v2/operations/put-v2-dinaco-id-syncticket-9f87e1207c7a.md): Dinaco: Teilnehmertickets synchronisieren ### dunning - [GET /v2/dunning](/docs/api/v2/operations/get-v2-dunning-bb8505475d4a.md): Mahnkonfiguration anzeigen - [GET /v2/dunning/config](/docs/api/v2/operations/get-v2-dunning-config-84da9600b9e3.md): Mahnkonfiguration anzeigen - [PUT /v2/dunning/config](/docs/api/v2/operations/put-v2-dunning-config-edbd02da6e4d.md): Mahnregeln und Aktivierung speichern - [GET /v2/dunning/list](/docs/api/v2/operations/get-v2-dunning-list-8364828b9791.md): Letzte Mahnvorgänge anzeigen - [POST /v2/dunning/run](/docs/api/v2/operations/post-v2-dunning-run-e5a2170b4afc.md): Fällige Mahnungen ausführen - [GET /v2/dunning/{id}](/docs/api/v2/operations/get-v2-dunning-id-a4b598dee0a5.md): Mahnvorgang prüfen ### edsubscription - [GET /v2/edsubscription](/docs/api/v2/operations/get-v2-edsubscription-728229a317e7.md): Verfügbare edoobox-Abonnementpläne abrufen - [POST /v2/edsubscription](/docs/api/v2/operations/post-v2-edsubscription-6e75bd8a7dac.md): Kontopause oder Kontolöschung mit E-Mail-Bestätigung verwalten - [PUT /v2/edsubscription/account/edcard_setup](/docs/api/v2/operations/put-v2-edsubscription-account-edcard-setup-eef282c05dc0.md): Zahlungskarte über Stripe SetupIntent speichern - [PUT /v2/edsubscription/account/mwstnumber_verification](/docs/api/v2/operations/put-v2-edsubscription-account-mwstnumber-verification-29f0fef67dda.md): Umsatzsteuer-ID mit VIES prüfen - [PUT /v2/edsubscription/account/pay](/docs/api/v2/operations/put-v2-edsubscription-account-pay-cd9c3b1daffb.md): Guthaben für die edoobox-Kontoabrechnung bezahlen - [PUT /v2/edsubscription/account/promotion_code](/docs/api/v2/operations/put-v2-edsubscription-account-promotion-code-915f1f10e7ea.md): Verlängerungscode der Konto-Testphase einlösen - [PUT /v2/edsubscription/account/vat_relationship](/docs/api/v2/operations/put-v2-edsubscription-account-vat-relationship-744962f274c3.md): Geschäftsbeziehung B2B oder B2C festlegen - [GET /v2/edsubscription/list](/docs/api/v2/operations/get-v2-edsubscription-list-3bfb004acd30.md): Verfügbare edoobox-Abonnementpläne abrufen - [GET /v2/edsubscription/list/edcard](/docs/api/v2/operations/get-v2-edsubscription-list-edcard-d326f88ccdb9.md): Gespeicherte Konto-Zahlungskarten abrufen - [PUT /v2/edsubscription/list/edcard_setup](/docs/api/v2/operations/put-v2-edsubscription-list-edcard-setup-a8a3366f31e0.md): Zahlungskarte über Stripe SetupIntent speichern - [GET /v2/edsubscription/list/edinvoice](/docs/api/v2/operations/get-v2-edsubscription-list-edinvoice-346905c4ec32.md): Konto-Abonnementrechnungen abrufen - [PUT /v2/edsubscription/list/mwstnumber_verification](/docs/api/v2/operations/put-v2-edsubscription-list-mwstnumber-verification-e65515eaa2d1.md): Umsatzsteuer-ID mit VIES prüfen - [PUT /v2/edsubscription/list/pay](/docs/api/v2/operations/put-v2-edsubscription-list-pay-df72ac35bcbd.md): Guthaben für die edoobox-Kontoabrechnung bezahlen - [PUT /v2/edsubscription/list/promotion_code](/docs/api/v2/operations/put-v2-edsubscription-list-promotion-code-90862d6be570.md): Verlängerungscode der Konto-Testphase einlösen - [GET /v2/edsubscription/list/skip_counter](/docs/api/v2/operations/get-v2-edsubscription-list-skip-counter-4e653230f9da.md): Protokoll übersprungener Zahlungssperren lesen - [PUT /v2/edsubscription/list/skip_counter](/docs/api/v2/operations/put-v2-edsubscription-list-skip-counter-6c67d90bcc8d.md): Verfügbare Zahlungssperre einmal überspringen - [GET /v2/edsubscription/list/subscriptions_verification](/docs/api/v2/operations/get-v2-edsubscription-list-subscriptions-verification-1ee55bd24db7.md): Tatsächliche Abonnementnutzung prüfen - [PUT /v2/edsubscription/list/vat_relationship](/docs/api/v2/operations/put-v2-edsubscription-list-vat-relationship-8ce48f47ba95.md): Geschäftsbeziehung B2B oder B2C festlegen - [GET /v2/edsubscription/{id}](/docs/api/v2/operations/get-v2-edsubscription-id-bbc8545b20b7.md): Verfügbare edoobox-Abonnementpläne abrufen - [PUT /v2/edsubscription/{id}/edcard](/docs/api/v2/operations/put-v2-edsubscription-id-edcard-0d5f1d4dc7f5.md): Status und Zahlungsrolle gespeicherter Karte ändern - [DELETE /v2/edsubscription/{id}/edcard](/docs/api/v2/operations/delete-v2-edsubscription-id-edcard-5adc49d409ab.md): Gespeicherte Konto-Zahlungskarte entfernen - [GET /v2/edsubscription/{id}/pay/invoice.pdf](/docs/api/v2/operations/get-v2-edsubscription-id-pay-invoice-pdf-6387ffe2c767.md): Konto-Abonnementrechnung als PDF laden - [GET /v2/edsubscription/{id}/pay/receipt.pdf](/docs/api/v2/operations/get-v2-edsubscription-id-pay-receipt-pdf-04dc448718fa.md): Konto-Zahlungsquittung als PDF laden - [PUT /v2/edsubscription/{id}/select](/docs/api/v2/operations/put-v2-edsubscription-id-select-d878ca07e31c.md): edoobox-Abonnementplan wechseln ### edtransaction - [GET /v2/edtransaction](/docs/api/v2/operations/get-v2-edtransaction-23f23d3ea24b.md): edoobox-Kontobuchungen anzeigen - [GET /v2/edtransaction/list](/docs/api/v2/operations/get-v2-edtransaction-list-aa02ca9e1e9c.md): edoobox-Kontobuchungen anzeigen - [GET /v2/edtransaction/list/balance](/docs/api/v2/operations/get-v2-edtransaction-list-balance-12b2dff7f9ae.md): edoobox-Kontostand zum Stichtag berechnen - [GET /v2/edtransaction/list/table](/docs/api/v2/operations/get-v2-edtransaction-list-table-46e78ca71416.md): Monatlichen edoobox-Kontoauszug anzeigen - [GET /v2/edtransaction/{id}](/docs/api/v2/operations/get-v2-edtransaction-id-f9151c1ab044.md): edoobox-Kontobuchung prüfen - [GET /v2/edtransaction/{id}/business_transaction](/docs/api/v2/operations/get-v2-edtransaction-id-business-transaction-483c5bfbfd3c.md): Buchungsgebühren einer Kontobelastung aufschlüsseln - [GET /v2/edtransaction/{id}/calls_sms_letters_transaction](/docs/api/v2/operations/get-v2-edtransaction-id-calls-sms-letters-transaction-793738828ab4.md): Monatliche Nachrichtenkosten aufschlüsseln - [GET /v2/edtransaction/{id}/transaction_invoice](/docs/api/v2/operations/get-v2-edtransaction-id-transaction-invoice-557f95a7e88c.md): edoobox-Kontorechnung herunterladen - [GET /v2/edtransaction/{id}/transaction_receipt](/docs/api/v2/operations/get-v2-edtransaction-id-transaction-receipt-0bf437cbb6a7.md): Zahlungsbeleg zum edoobox-Konto laden ### emailtemplate - [GET /v2/emailtemplate](/docs/api/v2/operations/get-v2-emailtemplate-6384f83a581a.md): E-Mail- und PDF-Vorlagen auflisten - [PUT /v2/emailtemplate](/docs/api/v2/operations/put-v2-emailtemplate-cf7f6a6b981b.md): Inhalt einer Vorlagen-Sprachfassung ändern - [POST /v2/emailtemplate](/docs/api/v2/operations/post-v2-emailtemplate-745a478fde60.md): E-Mail- oder PDF-Vorlage in einer Sprache anlegen - [POST /v2/emailtemplate/attachment/add_object](/docs/api/v2/operations/post-v2-emailtemplate-attachment-add-object-e355b3d24730.md): PDF-Datei an einer Vorlage zum Upload reservieren - [GET /v2/emailtemplate/list](/docs/api/v2/operations/get-v2-emailtemplate-list-849e32ef406d.md): E-Mail- und PDF-Vorlagen auflisten - [GET /v2/emailtemplate/list/LUT](/docs/api/v2/operations/get-v2-emailtemplate-list-lut-b4d946924cd5.md): Aktive Systemvorlagen als Katalog lesen - [GET /v2/emailtemplate/list/PdfDefaultNames_LUT](/docs/api/v2/operations/get-v2-emailtemplate-list-pdfdefaultnames-lut-44de7e0a56f7.md): Standard-Dateinamen für PDF- und Kalenderanhänge lesen - [POST /v2/emailtemplate/list/add_object](/docs/api/v2/operations/post-v2-emailtemplate-list-add-object-cd136be0ddc5.md): PDF-Datei an einer Vorlage zum Upload reservieren - [GET /v2/emailtemplate/list/attachment](/docs/api/v2/operations/get-v2-emailtemplate-list-attachment-85d5815fda36.md): PDF-Anhänge auflisten - [GET /v2/emailtemplate/list/pdf_template](/docs/api/v2/operations/get-v2-emailtemplate-list-pdf-template-2247f9e3d5ff.md): PDF-Hintergründe auflisten - [GET /v2/emailtemplate/list/pdf_template_url](/docs/api/v2/operations/get-v2-emailtemplate-list-pdf-template-url-3266df3e35f8.md): Signierte Lese-URL einer Vorlagendatei abrufen - [POST /v2/emailtemplate/pdftemplate/add_object](/docs/api/v2/operations/post-v2-emailtemplate-pdftemplate-add-object-7c22f7d7ddde.md): PDF-Datei an einer Vorlage zum Upload reservieren - [GET /v2/emailtemplate/{id}](/docs/api/v2/operations/get-v2-emailtemplate-id-d1bcdb7630c0.md): Vorlage lesen - [PUT /v2/emailtemplate/{id}](/docs/api/v2/operations/put-v2-emailtemplate-id-6a6781ff0956.md): Inhalt einer Vorlagen-Sprachfassung ändern - [GET /v2/emailtemplate/{id}/attachment_url](/docs/api/v2/operations/get-v2-emailtemplate-id-attachment-url-ee9afda5b1c6.md): Signierte Lese-URL einer Vorlagendatei abrufen - [GET /v2/emailtemplate/{id}/pdf_template_url](/docs/api/v2/operations/get-v2-emailtemplate-id-pdf-template-url-ec10f9dce8b6.md): Signierte Lese-URL einer Vorlagendatei abrufen - [GET /v2/emailtemplate/{id}/placeholders](/docs/api/v2/operations/get-v2-emailtemplate-id-placeholders-484c19ef2faf.md): Platzhalter einer Vorlagenfamilie lesen - [GET /v2/emailtemplate/{id}/preview](/docs/api/v2/operations/get-v2-emailtemplate-id-preview-f1b320d34886.md): Vorlage mit Beispieldaten als HTML oder PDF rendern - [GET /v2/emailtemplate/{id}/previewPDF](/docs/api/v2/operations/get-v2-emailtemplate-id-previewpdf-122f4a5aa87f.md): Vorlage mit Beispieldaten als HTML oder PDF rendern - [DELETE /v2/emailtemplate/{id}/remove_object](/docs/api/v2/operations/delete-v2-emailtemplate-id-remove-object-9683ca132ba1.md): PDF-Datei und Upload-Reservierung entfernen - [PUT /v2/emailtemplate/{id}/verify_object](/docs/api/v2/operations/put-v2-emailtemplate-id-verify-object-90ceb0770062.md): PDF-Upload prüfen und freigeben ### exportmanager - [GET /v2/exportmanager](/docs/api/v2/operations/get-v2-exportmanager-e936c59e176b.md): Gespeicherte individuelle Exportdefinitionen lesen - [PUT /v2/exportmanager](/docs/api/v2/operations/put-v2-exportmanager-ff69d4b83871.md): Individuelle Exportdefinition ersetzen - [POST /v2/exportmanager](/docs/api/v2/operations/post-v2-exportmanager-fb43893961c0.md): Individuelle Exportdefinition anlegen - [DELETE /v2/exportmanager](/docs/api/v2/operations/delete-v2-exportmanager-3e9c96d7ffd8.md): Individuelle Exportdefinition löschen - [GET /v2/exportmanager/list](/docs/api/v2/operations/get-v2-exportmanager-list-9fa16dcc28e1.md): Gespeicherte individuelle Exportdefinitionen lesen - [POST /v2/exportmanager/list/analyze](/docs/api/v2/operations/post-v2-exportmanager-list-analyze-87a67bb6144b.md): Exportentwurf und Datenabhängigkeiten prüfen - [GET /v2/exportmanager/list/catalog](/docs/api/v2/operations/get-v2-exportmanager-list-catalog-d42de9d37e09.md): Felder und Eingaben individueller Exporte entdecken - [GET /v2/exportmanager/list/download](/docs/api/v2/operations/get-v2-exportmanager-list-download-d2b559e35659.md): Fertige CSV-Exportdatei herunterladen - [POST /v2/exportmanager/list/preview](/docs/api/v2/operations/post-v2-exportmanager-list-preview-c33f7022a988.md): Exportvorschau lesen - [POST /v2/exportmanager/list/run](/docs/api/v2/operations/post-v2-exportmanager-list-run-b180a3d2134b.md): Individuellen CSV-Export als Hintergrundlauf starten - [GET /v2/exportmanager/list/run_status](/docs/api/v2/operations/get-v2-exportmanager-list-run-status-ecf37ded81ab.md): Exportlauf-Fortschritt lesen - [GET /v2/exportmanager/{id}](/docs/api/v2/operations/get-v2-exportmanager-id-4ee9aa6b387f.md): Gespeicherte individuelle Exportdefinitionen lesen - [PUT /v2/exportmanager/{id}](/docs/api/v2/operations/put-v2-exportmanager-id-8679787063d3.md): Individuelle Exportdefinition ersetzen - [DELETE /v2/exportmanager/{id}](/docs/api/v2/operations/delete-v2-exportmanager-id-ca2b0fd5aaa1.md): Individuelle Exportdefinition löschen ### fee - [GET /v2/fee](/docs/api/v2/operations/get-v2-fee-4afbae84e222.md): Gebühren und offene Beträge abrufen - [GET /v2/fee/list](/docs/api/v2/operations/get-v2-fee-list-6595c8f1b49e.md): Gebühren und offene Beträge abrufen - [PUT /v2/fee/list/cancel](/docs/api/v2/operations/put-v2-fee-list-cancel-a4a6ddcc971b.md): Gebühr stornieren oder gegenbuchen - [PUT /v2/fee/list/invoice](/docs/api/v2/operations/put-v2-fee-list-invoice-e51b69c7d364.md): Gebührenrechnung erstellen - [PUT /v2/fee/list/send_payment_reminder](/docs/api/v2/operations/put-v2-fee-list-send-payment-reminder-c77c270f80d2.md): Zahlungserinnerung zur Gebühr versenden - [GET /v2/fee/{id}](/docs/api/v2/operations/get-v2-fee-id-23193f431ade.md): Gebühren und offene Beträge abrufen - [PUT /v2/fee/{id}/cancel](/docs/api/v2/operations/put-v2-fee-id-cancel-1e05d0729f36.md): Gebühr stornieren oder gegenbuchen - [PUT /v2/fee/{id}/invoice](/docs/api/v2/operations/put-v2-fee-id-invoice-3b4d78f5e1c5.md): Gebührenrechnung erstellen - [PUT /v2/fee/{id}/send_payment_reminder](/docs/api/v2/operations/put-v2-fee-id-send-payment-reminder-f6129093d327.md): Zahlungserinnerung zur Gebühr versenden ### file - [GET /v2/file](/docs/api/v2/operations/get-v2-file-d83027c3b35c.md): Teilnehmerdatei auflisten - [PUT /v2/file](/docs/api/v2/operations/put-v2-file-51a5213a899f.md): Teilnehmerdatei ändern - [POST /v2/file](/docs/api/v2/operations/post-v2-file-bc6b3b276c98.md): Teilnehmerdatei anlegen - [DELETE /v2/file](/docs/api/v2/operations/delete-v2-file-e51a97cc15b7.md): Teilnehmerdatei löschen - [GET /v2/file/list](/docs/api/v2/operations/get-v2-file-list-fdbf54a92a5a.md): Teilnehmerdatei auflisten - [GET /v2/file/list/url](/docs/api/v2/operations/get-v2-file-list-url-4534976f4414.md): Signierte Download-Adresse einer Teilnehmerdatei beziehen - [GET /v2/file/{id}](/docs/api/v2/operations/get-v2-file-id-0e36069a36fd.md): Teilnehmerdatei lesen - [PUT /v2/file/{id}](/docs/api/v2/operations/put-v2-file-id-95dbc2c64d1a.md): Teilnehmerdatei ändern - [DELETE /v2/file/{id}](/docs/api/v2/operations/delete-v2-file-id-f8b547a20d48.md): Teilnehmerdatei löschen - [GET /v2/file/{id}/url](/docs/api/v2/operations/get-v2-file-id-url-036c8bf134be.md): Signierte Download-Adresse einer Teilnehmerdatei beziehen ### invoice - [GET /v2/invoice](/docs/api/v2/operations/get-v2-invoice-b07d1255f372.md): Rechnungen des Kontos anzeigen - [PUT /v2/invoice](/docs/api/v2/operations/put-v2-invoice-37f38c23b19e.md): Fälligkeit einer Rechnung ändern - [POST /v2/invoice](/docs/api/v2/operations/post-v2-invoice-cc6a4b5ea7b8.md): Rechnung für abrechenbare Positionen erstellen - [GET /v2/invoice/list](/docs/api/v2/operations/get-v2-invoice-list-a420c5b30369.md): Rechnungen des Kontos anzeigen - [GET /v2/invoice/list/invoice.pdf](/docs/api/v2/operations/get-v2-invoice-list-invoice-pdf-ffdb117eb9ee.md): Rechnungsdokument herunterladen - [GET /v2/invoice/list/invoice_collective_pdf_export](/docs/api/v2/operations/get-v2-invoice-list-invoice-collective-pdf-export-dae20655912a.md): Rechnungen als Sammel-PDF exportieren - [GET /v2/invoice/list/invoice_collective_xrechnung_export](/docs/api/v2/operations/get-v2-invoice-list-invoice-collective-xrechnung-export-53f0c539d250.md): XRechnungen gesammelt exportieren - [GET /v2/invoice/list/receipt](/docs/api/v2/operations/get-v2-invoice-list-receipt-94fdfc78e4df.md): Rechnungsbelege und Stornorechnungen anzeigen - [GET /v2/invoice/list/receipt/invoicereceipt.pdf](/docs/api/v2/operations/get-v2-invoice-list-receipt-invoicereceipt-pdf-4f21cc2fc1d3.md): Rechnungsbeleg herunterladen - [GET /v2/invoice/list/refund_review](/docs/api/v2/operations/get-v2-invoice-list-refund-review-f5fce45baa46.md): Rechnungsdokumente nach Online-Erstattungen prüfen - [GET /v2/invoice/list/validation_qr_invoice](/docs/api/v2/operations/get-v2-invoice-list-validation-qr-invoice-60c5890f8129.md): QR-Rechnungsdaten validieren - [GET /v2/invoice/{id}](/docs/api/v2/operations/get-v2-invoice-id-2412017470c4.md): Rechnung des Kontos prüfen - [PUT /v2/invoice/{id}](/docs/api/v2/operations/put-v2-invoice-id-2d0f382f775d.md): Fälligkeit einer Rechnung ändern - [PUT /v2/invoice/{id}/cancel](/docs/api/v2/operations/put-v2-invoice-id-cancel-3c45a413da1c.md): Rechnung stornieren - [GET /v2/invoice/{id}/dashboard](/docs/api/v2/operations/get-v2-invoice-id-dashboard-018caad6cccd.md): Rechnungsübersicht zusammenstellen - [GET /v2/invoice/{id}/data](/docs/api/v2/operations/get-v2-invoice-id-data-50d358d39d8c.md): Rechnungsdetails und Zahlungspositionen anzeigen - [PUT /v2/invoice/{id}/deposit](/docs/api/v2/operations/put-v2-invoice-id-deposit-1f8b21010a04.md): Zahlungseingang auf Rechnung verbuchen - [GET /v2/invoice/{id}/invoice.pdf](/docs/api/v2/operations/get-v2-invoice-id-invoice-pdf-9130e36c9d26.md): Rechnungsdokument herunterladen - [GET /v2/invoice/{id}/invoice.pdf/{cacheToken}](/docs/api/v2/operations/get-v2-invoice-id-invoice-pdf-cachetoken-79f561eabf05.md): Rechnungsdokument herunterladen - [GET /v2/invoice/{id}/receipt/invoicereceipt.pdf](/docs/api/v2/operations/get-v2-invoice-id-receipt-invoicereceipt-pdf-e06e6c125022.md): Rechnungsbeleg herunterladen - [GET /v2/invoice/{id}/receipt/invoicereceipt.pdf/{cacheToken}](/docs/api/v2/operations/get-v2-invoice-id-receipt-invoicereceipt-pdf-cachetoken-5d75f01a8e87.md): Rechnungsbeleg herunterladen - [PUT /v2/invoice/{id}/refresh](/docs/api/v2/operations/put-v2-invoice-id-refresh-237ec2a53c7f.md): Rechnung aus Positionen neu berechnen - [PUT /v2/invoice/{id}/refresh_payments](/docs/api/v2/operations/put-v2-invoice-id-refresh-payments-81fc1b97f1b7.md): Rechnungszahlungen und Dokument abgleichen - [GET /v2/invoice/{id}/refresh_suggested](/docs/api/v2/operations/get-v2-invoice-id-refresh-suggested-7bec6499b5f0.md): Änderungsbedarf einer Rechnung prüfen - [PUT /v2/invoice/{id}/refund](/docs/api/v2/operations/put-v2-invoice-id-refund-8ba090828509.md): Manuelle Rechnungserstattung verbuchen - [PUT /v2/invoice/{id}/send](/docs/api/v2/operations/put-v2-invoice-id-send-8dfe433f4a67.md): Rechnung versenden - [GET /v2/invoice/{id}/transaction_template](/docs/api/v2/operations/get-v2-invoice-id-transaction-template-c6bf7317fa9b.md): Rechnungspositionen als HTML-Vorlage laden - [GET /v2/invoice/{id}/validation_qr_invoice](/docs/api/v2/operations/get-v2-invoice-id-validation-qr-invoice-2a0b185d4e13.md): QR-Rechnungsdaten validieren ### layout - [GET /v2/layout/list/pdf](/docs/api/v2/operations/get-v2-layout-list-pdf-76bbfdbec515.md): PDF-Layout auflisten - [POST /v2/layout/list/pdf](/docs/api/v2/operations/post-v2-layout-list-pdf-978cfb8b54ef.md): PDF-Layout anlegen - [GET /v2/layout/list/pdf/placeholders](/docs/api/v2/operations/get-v2-layout-list-pdf-placeholders-2661f3ba3812.md): Platzhalter für PDF- und Tabellenlayouts auflisten - [GET /v2/layout/list/pdfattachment](/docs/api/v2/operations/get-v2-layout-list-pdfattachment-b064c9c3ddae.md): PDF-Anhang auflisten - [POST /v2/layout/list/pdfattachment](/docs/api/v2/operations/post-v2-layout-list-pdfattachment-b2dc7f6a9df5.md): PDF-Anhang für einen PDF-Upload reservieren - [GET /v2/layout/list/pdftemplate](/docs/api/v2/operations/get-v2-layout-list-pdftemplate-b83eefc5690f.md): PDF-Hintergrundvorlage auflisten - [POST /v2/layout/list/pdftemplate](/docs/api/v2/operations/post-v2-layout-list-pdftemplate-72d55a7b59eb.md): PDF-Hintergrundvorlage für einen PDF-Upload reservieren - [GET /v2/layout/list/table](/docs/api/v2/operations/get-v2-layout-list-table-e5cc37200307.md): Tabellenlayout auflisten - [POST /v2/layout/list/table](/docs/api/v2/operations/post-v2-layout-list-table-b984f2e14c61.md): Tabellenlayout anlegen - [GET /v2/layout/list/table/placeholders](/docs/api/v2/operations/get-v2-layout-list-table-placeholders-1bc1e9f8c047.md): Platzhalter für PDF- und Tabellenlayouts auflisten - [POST /v2/layout/post/pdf](/docs/api/v2/operations/post-v2-layout-post-pdf-9ecf79a3d66e.md): PDF-Layout anlegen - [POST /v2/layout/post/pdfattachment/add_object](/docs/api/v2/operations/post-v2-layout-post-pdfattachment-add-object-906ebca74e4e.md): PDF-Anhang für einen PDF-Upload reservieren - [POST /v2/layout/post/pdftemplate/add_object](/docs/api/v2/operations/post-v2-layout-post-pdftemplate-add-object-99afbd84f107.md): PDF-Hintergrundvorlage für einen PDF-Upload reservieren - [POST /v2/layout/post/table](/docs/api/v2/operations/post-v2-layout-post-table-e51196913cf0.md): Tabellenlayout anlegen - [GET /v2/layout/{id}/pdf](/docs/api/v2/operations/get-v2-layout-id-pdf-bf2ac3c6dfe1.md): PDF-Layout lesen - [PUT /v2/layout/{id}/pdf](/docs/api/v2/operations/put-v2-layout-id-pdf-078cec965c49.md): PDF-Layout ändern - [DELETE /v2/layout/{id}/pdf](/docs/api/v2/operations/delete-v2-layout-id-pdf-8e9345149af9.md): PDF-Layout entfernen - [GET /v2/layout/{id}/pdf/placeholders](/docs/api/v2/operations/get-v2-layout-id-pdf-placeholders-a4d5f2b2fab0.md): Platzhalter für PDF- und Tabellenlayouts auflisten - [GET /v2/layout/{id}/pdf/preview](/docs/api/v2/operations/get-v2-layout-id-pdf-preview-b65913c5f0df.md): PDF-Layout mit Beispieldaten ansehen - [GET /v2/layout/{id}/pdf/render](/docs/api/v2/operations/get-v2-layout-id-pdf-render-46791b43674f.md): PDF aus Layout und Angebotsdaten erzeugen - [POST /v2/layout/{id}/pdf/render](/docs/api/v2/operations/post-v2-layout-id-pdf-render-4616bc2d91c4.md): PDF aus Layout und Angebotsdaten erzeugen - [GET /v2/layout/{id}/pdf/render/{offer}](/docs/api/v2/operations/get-v2-layout-id-pdf-render-offer-c1ff588622a9.md): PDF aus Layout und Angebotsdaten erzeugen - [PUT /v2/layout/{id}/pdf/reset](/docs/api/v2/operations/put-v2-layout-id-pdf-reset-f8d7d00fe3f8.md): PDF-Layout auf Standardinhalt zurücksetzen - [PUT /v2/layout/{id}/pdf/verify_object](/docs/api/v2/operations/put-v2-layout-id-pdf-verify-object-4357a87026bd.md): Hochgeladenen PDF-Anhang oder Hintergrund prüfen - [GET /v2/layout/{id}/pdfattachment](/docs/api/v2/operations/get-v2-layout-id-pdfattachment-153e7ff45dcb.md): PDF-Anhang lesen - [DELETE /v2/layout/{id}/pdfattachment](/docs/api/v2/operations/delete-v2-layout-id-pdfattachment-dfc30e3779b4.md): PDF-Anhang entfernen - [DELETE /v2/layout/{id}/pdfattachment/remove_object](/docs/api/v2/operations/delete-v2-layout-id-pdfattachment-remove-object-8bbcc8b93040.md): PDF-Anhang entfernen - [PUT /v2/layout/{id}/pdfattachment/verify_object](/docs/api/v2/operations/put-v2-layout-id-pdfattachment-verify-object-f24aae8dfa00.md): Hochgeladenen PDF-Anhang oder Hintergrund prüfen - [GET /v2/layout/{id}/pdftemplate](/docs/api/v2/operations/get-v2-layout-id-pdftemplate-ba4788c4fc24.md): PDF-Hintergrundvorlage lesen - [DELETE /v2/layout/{id}/pdftemplate](/docs/api/v2/operations/delete-v2-layout-id-pdftemplate-50183bd4980c.md): PDF-Hintergrundvorlage entfernen - [DELETE /v2/layout/{id}/pdftemplate/remove_object](/docs/api/v2/operations/delete-v2-layout-id-pdftemplate-remove-object-9a9e0c86aa08.md): PDF-Hintergrundvorlage entfernen - [PUT /v2/layout/{id}/pdftemplate/verify_object](/docs/api/v2/operations/put-v2-layout-id-pdftemplate-verify-object-7406a849ccc4.md): Hochgeladenen PDF-Anhang oder Hintergrund prüfen - [GET /v2/layout/{id}/table](/docs/api/v2/operations/get-v2-layout-id-table-816e262b2b4b.md): Tabellenlayout lesen - [PUT /v2/layout/{id}/table](/docs/api/v2/operations/put-v2-layout-id-table-249b3a596077.md): Tabellenlayout ändern - [DELETE /v2/layout/{id}/table](/docs/api/v2/operations/delete-v2-layout-id-table-33e2e69e23bf.md): Tabellenlayout entfernen - [GET /v2/layout/{id}/table/placeholders](/docs/api/v2/operations/get-v2-layout-id-table-placeholders-6af8bcb625a0.md): Platzhalter für PDF- und Tabellenlayouts auflisten - [PUT /v2/layout/{id}/table/reset](/docs/api/v2/operations/put-v2-layout-id-table-reset-cee1ab636216.md): Tabellenlayout auf Standardinhalt zurücksetzen - [PUT /v2/layout/{id}/table/verify_object](/docs/api/v2/operations/put-v2-layout-id-table-verify-object-4bb140e63c4e.md): Hochgeladenen PDF-Anhang oder Hintergrund prüfen ### membershipperiod - [GET /v2/membershipperiod](/docs/api/v2/operations/get-v2-membershipperiod-e4c576212d07.md): Abrechnungsperioden einer Mitgliedschaft lesen - [GET /v2/membershipperiod/list](/docs/api/v2/operations/get-v2-membershipperiod-list-4f2c6605f7de.md): Abrechnungsperioden einer Mitgliedschaft lesen - [GET /v2/membershipperiod/{id}](/docs/api/v2/operations/get-v2-membershipperiod-id-902b1b664247.md): Periodenbeleg lesen ### membershipplan - [GET /v2/membershipplan](/docs/api/v2/operations/get-v2-membershipplan-72db4f3201a8.md): Mitgliedschaftspläne suchen - [PUT /v2/membershipplan](/docs/api/v2/operations/put-v2-membershipplan-93fa91761b50.md): Mitgliedschaftsplan aktualisieren - [POST /v2/membershipplan](/docs/api/v2/operations/post-v2-membershipplan-fc2811bf7d6d.md): Mitgliedschaftsplan als Entwurf erstellen - [DELETE /v2/membershipplan](/docs/api/v2/operations/delete-v2-membershipplan-6e09be122bc7.md): Mitgliedschaftsplan archivieren - [GET /v2/membershipplan/list](/docs/api/v2/operations/get-v2-membershipplan-list-b28bc532e88b.md): Mitgliedschaftspläne suchen - [GET /v2/membershipplan/list/dashboard](/docs/api/v2/operations/get-v2-membershipplan-list-dashboard-f977ffe094ea.md): Kennzahlen eines Mitgliedschaftsplans lesen - [GET /v2/membershipplan/list/publish-preflight](/docs/api/v2/operations/get-v2-membershipplan-list-publish-preflight-b3284d70e117.md): Veröffentlichungsreife eines Mitgliedschaftsplans prüfen - [GET /v2/membershipplan/{id}](/docs/api/v2/operations/get-v2-membershipplan-id-0e6fef2d9ab3.md): Mitgliedschaftsplan im Detail lesen - [PUT /v2/membershipplan/{id}](/docs/api/v2/operations/put-v2-membershipplan-id-342b776f06e6.md): Mitgliedschaftsplan aktualisieren - [DELETE /v2/membershipplan/{id}](/docs/api/v2/operations/delete-v2-membershipplan-id-4d73239c30bc.md): Mitgliedschaftsplan archivieren - [GET /v2/membershipplan/{id}/dashboard](/docs/api/v2/operations/get-v2-membershipplan-id-dashboard-ba59684a192b.md): Kennzahlen eines Mitgliedschaftsplans lesen - [PUT /v2/membershipplan/{id}/internal](/docs/api/v2/operations/put-v2-membershipplan-id-internal-2eded3f904ee.md): Mitgliedschaftsplan intern verwenden - [PUT /v2/membershipplan/{id}/publish](/docs/api/v2/operations/put-v2-membershipplan-id-publish-b55b7e9a81c7.md): Mitgliedschaftsplan veröffentlichen - [GET /v2/membershipplan/{id}/publish-preflight](/docs/api/v2/operations/get-v2-membershipplan-id-publish-preflight-87a041d1d877.md): Veröffentlichungsreife eines Mitgliedschaftsplans prüfen - [PUT /v2/membershipplan/{id}/unpublish](/docs/api/v2/operations/put-v2-membershipplan-id-unpublish-8f1561f7f123.md): Mitgliedschaftsplan zurückziehen ### membershipredemption - [GET /v2/membershipredemption](/docs/api/v2/operations/get-v2-membershipredemption-c67ddc8f76bf.md): Nutzungsbuchungen einer Mitgliedschaft lesen - [GET /v2/membershipredemption/list](/docs/api/v2/operations/get-v2-membershipredemption-list-e4511090ab1a.md): Nutzungsbuchungen einer Mitgliedschaft lesen - [GET /v2/membershipredemption/{id}](/docs/api/v2/operations/get-v2-membershipredemption-id-45f69ab46a94.md): Nutzungsbeleg lesen ### note - [GET /v2/note](/docs/api/v2/operations/get-v2-note-af1df5e62e80.md): Notizen auflisten - [PUT /v2/note](/docs/api/v2/operations/put-v2-note-9a7ea1007c53.md): Notiz ändern - [POST /v2/note](/docs/api/v2/operations/post-v2-note-383c8be08314.md): Notiz anlegen - [GET /v2/note/list](/docs/api/v2/operations/get-v2-note-list-53b988db8530.md): Notizen auflisten - [GET /v2/note/{id}](/docs/api/v2/operations/get-v2-note-id-7a2d464c0fd6.md): Notiz lesen - [PUT /v2/note/{id}](/docs/api/v2/operations/put-v2-note-id-5ef8bb47a4ba.md): Notiz ändern - [PUT /v2/note/{id}/isdone](/docs/api/v2/operations/put-v2-note-id-isdone-5a5a8d6def8f.md): TODO erledigen und gegebenenfalls Stornierung freigeben ### offer - [GET /v2/offer](/docs/api/v2/operations/get-v2-offer-2d86092592a6.md): Angebote auflisten - [PUT /v2/offer](/docs/api/v2/operations/put-v2-offer-cb4e46b184eb.md): Angebot aktualisieren - [POST /v2/offer](/docs/api/v2/operations/post-v2-offer-1826a986b190.md): Angebot anlegen - [GET /v2/offer/list](/docs/api/v2/operations/get-v2-offer-list-f8ea4c8def99.md): Angebote auflisten - [GET /v2/offer/list/export](/docs/api/v2/operations/get-v2-offer-list-export-d25e2e49bf7b.md): Angebote für einen Zeitraum exportieren - [GET /v2/offer/list/export1.csv](/docs/api/v2/operations/get-v2-offer-list-export1-csv-98b789bd277a.md): Teilnehmerexport 1 als CSV herunterladen - [GET /v2/offer/list/export1.ods](/docs/api/v2/operations/get-v2-offer-list-export1-ods-f76479ca1426.md): Teilnehmerexport 1 als ODS herunterladen - [GET /v2/offer/list/export1.xlsx](/docs/api/v2/operations/get-v2-offer-list-export1-xlsx-9aace58599e1.md): Teilnehmerexport 1 als XLSX herunterladen - [GET /v2/offer/list/export2.csv](/docs/api/v2/operations/get-v2-offer-list-export2-csv-337c780394d1.md): Teilnehmerexport 2 als CSV herunterladen - [GET /v2/offer/list/export2.ods](/docs/api/v2/operations/get-v2-offer-list-export2-ods-be529c937f87.md): Teilnehmerexport 2 als ODS herunterladen - [GET /v2/offer/list/export2.xlsx](/docs/api/v2/operations/get-v2-offer-list-export2-xlsx-bc0334fd036f.md): Teilnehmerexport 2 als XLSX herunterladen - [POST /v2/offer/list/files](/docs/api/v2/operations/post-v2-offer-list-files-8c0d9c35a40e.md): Datei zu einer Ressource hochladen - [GET /v2/offer/list/files/list](/docs/api/v2/operations/get-v2-offer-list-files-list-256519813dcc.md): Angebotsdateien auflisten - [GET /v2/offer/list/files/{fileId}/download](/docs/api/v2/operations/get-v2-offer-list-files-fileid-download-f09177ad2886.md): Datei einer Ressource herunterladen - [GET /v2/offer/list/globaltable](/docs/api/v2/operations/get-v2-offer-list-globaltable-7bd6eb9a04cd.md): Angebotsübersicht mit Belegungsdaten abrufen - [POST /v2/offer/list/ical_feed](/docs/api/v2/operations/post-v2-offer-list-ical-feed-b9e056f98ad0.md): Angebot über den Legacy-Pfad anlegen - [GET /v2/offer/list/shareurl](/docs/api/v2/operations/get-v2-offer-list-shareurl-98ea8266a366.md): Freigabe- und Buchungslinks für Angebot abrufen - [GET /v2/offer/list/tree](/docs/api/v2/operations/get-v2-offer-list-tree-aacdc0cc2154.md): Gesamten Angebots- und Kategorienbaum abrufen - [DELETE /v2/offer/remove/files/{id}](/docs/api/v2/operations/delete-v2-offer-remove-files-id-cec9d016c1d0.md): Ressourcendatei löschen - [PUT /v2/offer/update/files/{id}](/docs/api/v2/operations/put-v2-offer-update-files-id-6daff87b1bcc.md): Dateimetadaten aktualisieren - [POST /v2/offer/upload/files](/docs/api/v2/operations/post-v2-offer-upload-files-197db9c80cfc.md): Datei zu einer Ressource hochladen - [GET /v2/offer/{id}](/docs/api/v2/operations/get-v2-offer-id-273a09c077e8.md): Angebot abrufen - [PUT /v2/offer/{id}](/docs/api/v2/operations/put-v2-offer-id-13b3f4bc2491.md): Angebot aktualisieren - [DELETE /v2/offer/{id}](/docs/api/v2/operations/delete-v2-offer-id-50c8817f7ba7.md): Angebot löschen - [GET /v2/offer/{id}/attendance_list.html](/docs/api/v2/operations/get-v2-offer-id-attendance-list-html-f24884f4eac5.md): Präsenzliste als HTML abrufen - [PUT /v2/offer/{id}/auto_archive](/docs/api/v2/operations/put-v2-offer-id-auto-archive-5226630dc3f2.md): Angebot automatisch archivieren - [PUT /v2/offer/{id}/auto_delete](/docs/api/v2/operations/put-v2-offer-id-auto-delete-555b83a2bf1b.md): Angebot in den Papierkorb verschieben - [POST /v2/offer/{id}/copy](/docs/api/v2/operations/post-v2-offer-id-copy-7e093e19f3a2.md): Angebot mit optionalen Terminen kopieren - [GET /v2/offer/{id}/dashboard](/docs/api/v2/operations/get-v2-offer-id-dashboard-fc09b5f59e04.md): Angebotsübersicht mit Buchungen und Auslastung abrufen - [PUT /v2/offer/{id}/deliverablesgroup](/docs/api/v2/operations/put-v2-offer-id-deliverablesgroup-1a8787de0a96.md): Zusatzleistungsgruppe einem Angebot zuordnen - [GET /v2/offer/{id}/files](/docs/api/v2/operations/get-v2-offer-id-files-229c48367140.md): Metadaten einer Angebotsdatei abrufen - [PUT /v2/offer/{id}/files](/docs/api/v2/operations/put-v2-offer-id-files-38ceae998231.md): Dateimetadaten aktualisieren - [DELETE /v2/offer/{id}/files](/docs/api/v2/operations/delete-v2-offer-id-files-a3f80930e5d7.md): Ressourcendatei löschen - [PUT /v2/offer/{id}/image](/docs/api/v2/operations/put-v2-offer-id-image-2ba69a5dd9a6.md): Angebot mit einem Bild versehen - [PUT /v2/offer/{id}/move](/docs/api/v2/operations/put-v2-offer-id-move-67d4211a9017.md): Angebot im Baum verschieben - [PUT /v2/offer/{id}/offerlist](/docs/api/v2/operations/put-v2-offer-id-offerlist-7766d25cf1df.md): Anfrageliste einem Angebot zuordnen - [GET /v2/offer/{id}/participant_information.html](/docs/api/v2/operations/get-v2-offer-id-participant-information-html-de395833b5aa.md): Teilnehmerinformationen als HTML abrufen - [PUT /v2/offer/{id}/questiongroup](/docs/api/v2/operations/put-v2-offer-id-questiongroup-fc442a41da0c.md): Zusatzfragengruppe einem Angebot zuordnen - [GET /v2/offer/{id}/shareurl](/docs/api/v2/operations/get-v2-offer-id-shareurl-82b4a3ca012c.md): Freigabe- und Buchungslinks für Angebot abrufen - [GET /v2/offer/{id}/trash_permission](/docs/api/v2/operations/get-v2-offer-id-trash-permission-0b66c78bc367.md): Papierkorbfreigabe für ein Angebot prüfen ### offerdef - [GET /v2/offerdef](/docs/api/v2/operations/get-v2-offerdef-14aa12915519.md): Angebotsbezeichnungen auflisten - [GET /v2/offerdef/list](/docs/api/v2/operations/get-v2-offerdef-list-9c4269b656bf.md): Angebotsbezeichnungen auflisten - [GET /v2/offerdef/{id}](/docs/api/v2/operations/get-v2-offerdef-id-9bd526576792.md): Angebotsbezeichnung abrufen ### offerlist - [GET /v2/offerlist](/docs/api/v2/operations/get-v2-offerlist-90740d61190a.md): Anfragelisten auflisten - [PUT /v2/offerlist](/docs/api/v2/operations/put-v2-offerlist-2aadb6b4ef17.md): Anfrageliste aktualisieren - [POST /v2/offerlist](/docs/api/v2/operations/post-v2-offerlist-4d7c8818d074.md): Anfrageliste anlegen - [DELETE /v2/offerlist](/docs/api/v2/operations/delete-v2-offerlist-77b0a3c4ad04.md): Anfrageliste löschen - [GET /v2/offerlist/list](/docs/api/v2/operations/get-v2-offerlist-list-1fe43cbe537e.md): Anfragelisten auflisten - [GET /v2/offerlist/{id}](/docs/api/v2/operations/get-v2-offerlist-id-5a41b9b59a3f.md): Anfrageliste abrufen - [PUT /v2/offerlist/{id}](/docs/api/v2/operations/put-v2-offerlist-id-66b6f1cdbc1a.md): Anfrageliste aktualisieren - [DELETE /v2/offerlist/{id}](/docs/api/v2/operations/delete-v2-offerlist-id-ebbfa9464e9e.md): Anfrageliste löschen - [PUT /v2/offerlist/{id}/send_reminder](/docs/api/v2/operations/put-v2-offerlist-id-send-reminder-f79bd04c1da1.md): Erinnerungen zur Anfrageliste versenden - [PUT /v2/offerlist/{id}/send_request](/docs/api/v2/operations/put-v2-offerlist-id-send-request-fdfbf0a7c19b.md): Anfrage-E-Mails versenden - [PUT /v2/offerlist/{id}/user](/docs/api/v2/operations/put-v2-offerlist-id-user-40f4f2ce2337.md): Teilnehmer in einer Anfrageliste verwalten ### outbox - [GET /v2/outbox](/docs/api/v2/operations/get-v2-outbox-0b62caa3d049.md): Nachrichten im Postausgang auflisten - [GET /v2/outbox/list](/docs/api/v2/operations/get-v2-outbox-list-38ccfbc1fd12.md): Nachrichten im Postausgang auflisten - [GET /v2/outbox/list/view](/docs/api/v2/operations/get-v2-outbox-list-view-a2000ba417be.md): Gespeicherte E-Mail als HTML anzeigen - [GET /v2/outbox/{id}](/docs/api/v2/operations/get-v2-outbox-id-8280facd1e4e.md): Nachricht im postausgang lesen - [GET /v2/outbox/{id}/view](/docs/api/v2/operations/get-v2-outbox-id-view-e1bf38e80591.md): Gespeicherte E-Mail als HTML anzeigen ### paymentsystem - [GET /v2/paymentsystem](/docs/api/v2/operations/get-v2-paymentsystem-9e94fb12f51a.md): Zahlungs- und Rechnungseinstellungen anzeigen - [PUT /v2/paymentsystem](/docs/api/v2/operations/put-v2-paymentsystem-b4bb32fb9cfa.md): Zahlungs- und Rechnungseinstellungen speichern - [GET /v2/paymentsystem/connect/paypal](/docs/api/v2/operations/get-v2-paymentsystem-connect-paypal-ffcba79a6240.md): PayPal-Händlerverbindung vorbereiten - [GET /v2/paymentsystem/connect/stripe](/docs/api/v2/operations/get-v2-paymentsystem-connect-stripe-dfae925a6e16.md): Stripe-Verbindung vorbereiten - [POST /v2/paymentsystem/connect/stripe_domain](/docs/api/v2/operations/post-v2-paymentsystem-connect-stripe-domain-b4a483fa851a.md): Stripe-Zahlungsdomain registrieren - [GET /v2/paymentsystem/list](/docs/api/v2/operations/get-v2-paymentsystem-list-6ebf17e0bf11.md): Zahlungs- und Rechnungseinstellungen anzeigen - [GET /v2/paymentsystem/list/fees_export](/docs/api/v2/operations/get-v2-paymentsystem-list-fees-export-bc72891c2473.md): Monatliche Zahlungsgebühren exportieren - [GET /v2/paymentsystem/list/fees_months](/docs/api/v2/operations/get-v2-paymentsystem-list-fees-months-b1d7172230ed.md): Monate mit Zahlungsgebühren anzeigen - [GET /v2/paymentsystem/list/paypal](/docs/api/v2/operations/get-v2-paymentsystem-list-paypal-6309ecdaeb45.md): PayPal-Händlerverbindung vorbereiten - [GET /v2/paymentsystem/list/paypal_status](/docs/api/v2/operations/get-v2-paymentsystem-list-paypal-status-72e7f814263b.md): PayPal-Händlerfreigabe aktualisieren - [GET /v2/paymentsystem/list/stripe](/docs/api/v2/operations/get-v2-paymentsystem-list-stripe-6cd82de845b5.md): Stripe-Verbindung vorbereiten - [GET /v2/paymentsystem/list/stripe_debug](/docs/api/v2/operations/get-v2-paymentsystem-list-stripe-debug-e1bcd745fef1.md): Stripe-Zahlungsbereitschaft diagnostizieren - [POST /v2/paymentsystem/list/stripe_domain](/docs/api/v2/operations/post-v2-paymentsystem-list-stripe-domain-2b9f0208e9ec.md): Stripe-Zahlungsdomain registrieren - [GET /v2/paymentsystem/{id}](/docs/api/v2/operations/get-v2-paymentsystem-id-b0d4628beddf.md): Zahlungs- und Rechnungseinstellungen anzeigen - [PUT /v2/paymentsystem/{id}](/docs/api/v2/operations/put-v2-paymentsystem-id-0887ec8eb7e5.md): Zahlungs- und Rechnungseinstellungen speichern - [PUT /v2/paymentsystem/{id}/paypal_deauthorize](/docs/api/v2/operations/put-v2-paymentsystem-id-paypal-deauthorize-005c3ba6dcf4.md): PayPal-Verbindung trennen - [PUT /v2/paymentsystem/{id}/stripe_deauthorize](/docs/api/v2/operations/put-v2-paymentsystem-id-stripe-deauthorize-7b755562e364.md): Stripe-Verbindung trennen ### permission - [GET /v2/permission](/docs/api/v2/operations/get-v2-permission-7f162e6de9e1.md): Berechtigungsprofile auflisten - [PUT /v2/permission](/docs/api/v2/operations/put-v2-permission-1243edfcb367.md): Berechtigungsprofil ändern - [POST /v2/permission](/docs/api/v2/operations/post-v2-permission-92bfd8875d84.md): Berechtigungsprofil anlegen - [DELETE /v2/permission](/docs/api/v2/operations/delete-v2-permission-3cd9a1fce9f8.md): Berechtigungsprofil entfernen - [GET /v2/permission/list](/docs/api/v2/operations/get-v2-permission-list-77e8d66405d1.md): Berechtigungsprofile auflisten - [GET /v2/permission/{id}](/docs/api/v2/operations/get-v2-permission-id-72c2a05ba817.md): Berechtigungsprofil lesen - [PUT /v2/permission/{id}](/docs/api/v2/operations/put-v2-permission-id-4147c6e23ffe.md): Berechtigungsprofil ändern - [DELETE /v2/permission/{id}](/docs/api/v2/operations/delete-v2-permission-id-814405365561.md): Berechtigungsprofil entfernen ### place - [GET /v2/place](/docs/api/v2/operations/get-v2-place-2024ee10d15d.md): Veranstaltungsorte auflisten - [PUT /v2/place](/docs/api/v2/operations/put-v2-place-2871d5923f31.md): Veranstaltungsort aktualisieren - [POST /v2/place](/docs/api/v2/operations/post-v2-place-8e540e55d838.md): Veranstaltungsort anlegen - [DELETE /v2/place](/docs/api/v2/operations/delete-v2-place-0c8e23f787cc.md): Veranstaltungsort löschen - [GET /v2/place/list](/docs/api/v2/operations/get-v2-place-list-a3abb934b3a1.md): Veranstaltungsorte auflisten - [GET /v2/place/list/notification](/docs/api/v2/operations/get-v2-place-list-notification-88e2fe30fd07.md): Ortsbenachrichtigungen auflisten - [PUT /v2/place/list/notification](/docs/api/v2/operations/put-v2-place-list-notification-841e390bd990.md): Ortsbenachrichtigung aktualisieren - [DELETE /v2/place/list/notification](/docs/api/v2/operations/delete-v2-place-list-notification-3326fe2142b4.md): Ortsbenachrichtigung löschen - [GET /v2/place/list/workload](/docs/api/v2/operations/get-v2-place-list-workload-82faf9d862ba.md): Belegung für Veranstaltungsort abrufen - [POST /v2/place/notification](/docs/api/v2/operations/post-v2-place-notification-455fa6ba12f7.md): Benachrichtigungsregel für einen Ort anlegen - [GET /v2/place/{id}](/docs/api/v2/operations/get-v2-place-id-17f530c2be3a.md): Veranstaltungsort abrufen - [PUT /v2/place/{id}](/docs/api/v2/operations/put-v2-place-id-80d0481d20e8.md): Veranstaltungsort aktualisieren - [DELETE /v2/place/{id}](/docs/api/v2/operations/delete-v2-place-id-ca32ab42accf.md): Veranstaltungsort löschen - [GET /v2/place/{id}/notification](/docs/api/v2/operations/get-v2-place-id-notification-786c8e61007d.md): Ortsbenachrichtigung abrufen - [PUT /v2/place/{id}/notification](/docs/api/v2/operations/put-v2-place-id-notification-5984613e39f8.md): Ortsbenachrichtigung aktualisieren - [DELETE /v2/place/{id}/notification](/docs/api/v2/operations/delete-v2-place-id-notification-b3f08c0c2c40.md): Ortsbenachrichtigung löschen - [GET /v2/place/{id}/workload](/docs/api/v2/operations/get-v2-place-id-workload-5a8b97a82d2d.md): Belegung für Veranstaltungsort abrufen ### plugin - [GET /v2/plugin](/docs/api/v2/operations/get-v2-plugin-891f6b572ec6.md): Plugin-Katalog und Vertragsbedingungen lesen - [PUT /v2/plugin/list/activate](/docs/api/v2/operations/put-v2-plugin-list-activate-6f00e9a3279e.md): Plugin aktivieren und Angebot bestätigen - [PUT /v2/plugin/list/deactivate](/docs/api/v2/operations/put-v2-plugin-list-deactivate-abb9a71b2f6e.md): Plugin kündigen oder deaktivieren - [GET /v2/plugin/list/preflight](/docs/api/v2/operations/get-v2-plugin-list-preflight-389b761979df.md): Plugin-Deaktivierung prüfen - [PUT /v2/plugin/{id}/activate](/docs/api/v2/operations/put-v2-plugin-id-activate-a29158928f59.md): Plugin aktivieren und Angebot bestätigen - [PUT /v2/plugin/{id}/deactivate](/docs/api/v2/operations/put-v2-plugin-id-deactivate-396fda63c311.md): Plugin kündigen oder deaktivieren - [GET /v2/plugin/{id}/preflight](/docs/api/v2/operations/get-v2-plugin-id-preflight-afcc98d7e886.md): Plugin-Deaktivierung prüfen ### pricecategory - [GET /v2/pricecategory](/docs/api/v2/operations/get-v2-pricecategory-28a65409e2b0.md): Preiskategorien auflisten - [PUT /v2/pricecategory](/docs/api/v2/operations/put-v2-pricecategory-0f85fb7354b8.md): Preiskategorie aktualisieren - [POST /v2/pricecategory](/docs/api/v2/operations/post-v2-pricecategory-01750cbd9ee4.md): Preiskategorie anlegen - [GET /v2/pricecategory/list](/docs/api/v2/operations/get-v2-pricecategory-list-42e7685d50ec.md): Preiskategorien auflisten - [GET /v2/pricecategory/{id}](/docs/api/v2/operations/get-v2-pricecategory-id-7cdab26e08db.md): Preiskategorie abrufen - [PUT /v2/pricecategory/{id}](/docs/api/v2/operations/put-v2-pricecategory-id-c23a67fd0482.md): Preiskategorie aktualisieren - [GET /v2/pricecategory/{id}/data](/docs/api/v2/operations/get-v2-pricecategory-id-data-f548299b4e23.md): Belegung und freie Plätze einer Preiskategorie prüfen ### promotion - [GET /v2/promotion](/docs/api/v2/operations/get-v2-promotion-3593210f4304.md): Rabatt- und Gutscheinkampagnen abrufen - [PUT /v2/promotion](/docs/api/v2/operations/put-v2-promotion-27edf63f6f17.md): Kampagne und Gültigkeitszuordnungen ändern - [POST /v2/promotion](/docs/api/v2/operations/post-v2-promotion-83291003b4be.md): Rabatt- oder Gutscheinkampagne anlegen - [GET /v2/promotion/list](/docs/api/v2/operations/get-v2-promotion-list-02667bd71c1e.md): Rabatt- und Gutscheinkampagnen abrufen - [GET /v2/promotion/{id}](/docs/api/v2/operations/get-v2-promotion-id-286f92fa657e.md): Rabatt- und Gutscheinkampagnen abrufen - [PUT /v2/promotion/{id}](/docs/api/v2/operations/put-v2-promotion-id-a7d53c3c5ad9.md): Kampagne und Gültigkeitszuordnungen ändern ### promotioncode - [GET /v2/promotioncode](/docs/api/v2/operations/get-v2-promotioncode-5d3900af6b33.md): Promotionscodes und Verwendungen abrufen - [PUT /v2/promotioncode](/docs/api/v2/operations/put-v2-promotioncode-b2f0f292976a.md): Code-Aktivstatus ändern oder Code-Eindeutigkeit prüfen - [POST /v2/promotioncode](/docs/api/v2/operations/post-v2-promotioncode-96cd4ac7a2ee.md): Promotionscode anlegen - [GET /v2/promotioncode/list](/docs/api/v2/operations/get-v2-promotioncode-list-61dd2b2df685.md): Promotionscodes und Verwendungen abrufen - [POST /v2/promotioncode/list/many](/docs/api/v2/operations/post-v2-promotioncode-list-many-1dca67d1ef3e.md): Zufällige Promotionscodes für eine Kampagne erzeugen - [GET /v2/promotioncode/{id}](/docs/api/v2/operations/get-v2-promotioncode-id-ee2f7e3bf1dd.md): Promotionscodes und Verwendungen abrufen - [PUT /v2/promotioncode/{id}](/docs/api/v2/operations/put-v2-promotioncode-id-25ac62ce760d.md): Code-Aktivstatus ändern oder Code-Eindeutigkeit prüfen ### publicfile - [GET /v2/publicfile](/docs/api/v2/operations/get-v2-publicfile-db7a7eaff160.md): öffentliche Datei auflisten - [PUT /v2/publicfile](/docs/api/v2/operations/put-v2-publicfile-c3f219bd734e.md): öffentliche Datei ändern - [POST /v2/publicfile](/docs/api/v2/operations/post-v2-publicfile-27d778580e09.md): öffentliche Datei anlegen - [DELETE /v2/publicfile](/docs/api/v2/operations/delete-v2-publicfile-79f9e5be38b9.md): öffentliche Datei löschen - [GET /v2/publicfile/list](/docs/api/v2/operations/get-v2-publicfile-list-934e9e0d1516.md): öffentliche Datei auflisten - [GET /v2/publicfile/{id}](/docs/api/v2/operations/get-v2-publicfile-id-3d497f7837e2.md): öffentliche Datei lesen - [PUT /v2/publicfile/{id}](/docs/api/v2/operations/put-v2-publicfile-id-7fa8c307cb2f.md): öffentliche Datei ändern - [DELETE /v2/publicfile/{id}](/docs/api/v2/operations/delete-v2-publicfile-id-4f87fa39f3f3.md): öffentliche Datei löschen ### pusherwss - [POST /v2/pusherwss](/docs/api/v2/operations/post-v2-pusherwss-3fc746d48df4.md): Privaten WebSocket-Kanal autorisieren - [POST /v2/pusherwss/list/supportid](/docs/api/v2/operations/post-v2-pusherwss-list-supportid-da39e51328dc.md): Support-ID an edoobox-Support senden - [POST /v2/pusherwss/send/supportid](/docs/api/v2/operations/post-v2-pusherwss-send-supportid-af7eb9edf690.md): Support-ID an edoobox-Support senden ### question - [GET /v2/question](/docs/api/v2/operations/get-v2-question-afe4b264a170.md): Antwortoption auflisten - [PUT /v2/question](/docs/api/v2/operations/put-v2-question-d7aa0790dd1d.md): Antwortoption ändern - [POST /v2/question](/docs/api/v2/operations/post-v2-question-e259f4853585.md): Antwortoption erstellen - [GET /v2/question/list](/docs/api/v2/operations/get-v2-question-list-fa1a73e4070c.md): Antwortoption auflisten - [GET /v2/question/{id}](/docs/api/v2/operations/get-v2-question-id-974cb4ed9b00.md): Antwortoption lesen - [PUT /v2/question/{id}](/docs/api/v2/operations/put-v2-question-id-d4f1a81fe585.md): Antwortoption ändern ### questiongroup - [GET /v2/questiongroup](/docs/api/v2/operations/get-v2-questiongroup-b1ca91f70a40.md): Zusatzfrage auflisten - [PUT /v2/questiongroup](/docs/api/v2/operations/put-v2-questiongroup-b309eec87d80.md): Zusatzfrage ändern - [POST /v2/questiongroup](/docs/api/v2/operations/post-v2-questiongroup-87b3f0a84aaa.md): Zusatzfrage erstellen - [DELETE /v2/questiongroup](/docs/api/v2/operations/delete-v2-questiongroup-cfef183d0f17.md): Zusatzfrage löschen - [GET /v2/questiongroup/list](/docs/api/v2/operations/get-v2-questiongroup-list-0315582b1f96.md): Zusatzfrage auflisten - [GET /v2/questiongroup/{id}](/docs/api/v2/operations/get-v2-questiongroup-id-1ef8d7b32f03.md): Zusatzfrage lesen - [PUT /v2/questiongroup/{id}](/docs/api/v2/operations/put-v2-questiongroup-id-2844869d1c9e.md): Zusatzfrage ändern - [DELETE /v2/questiongroup/{id}](/docs/api/v2/operations/delete-v2-questiongroup-id-28b18dcb6604.md): Zusatzfrage löschen ### room - [GET /v2/room](/docs/api/v2/operations/get-v2-room-c7aaf58b0c20.md): Räume auflisten - [PUT /v2/room](/docs/api/v2/operations/put-v2-room-5f95df92cd08.md): Raum aktualisieren - [POST /v2/room](/docs/api/v2/operations/post-v2-room-a62bee74bcdc.md): Raum anlegen - [GET /v2/room/list](/docs/api/v2/operations/get-v2-room-list-b108467eab6b.md): Räume auflisten - [GET /v2/room/list/workload](/docs/api/v2/operations/get-v2-room-list-workload-26a8a0208519.md): Belegung für Raum abrufen - [GET /v2/room/{id}](/docs/api/v2/operations/get-v2-room-id-df25cd511096.md): Raum abrufen - [PUT /v2/room/{id}](/docs/api/v2/operations/put-v2-room-id-6b375787a753.md): Raum aktualisieren - [GET /v2/room/{id}/workload](/docs/api/v2/operations/get-v2-room-id-workload-0ffe9a13e77f.md): Belegung für Raum abrufen ### saml - [GET /v2/saml](/docs/api/v2/operations/get-v2-saml-ffd41d79e539.md): SAML-Provider auflisten - [PUT /v2/saml](/docs/api/v2/operations/put-v2-saml-0693d57c116d.md): SAML-Provider ändern - [POST /v2/saml](/docs/api/v2/operations/post-v2-saml-2d3c92ce3e9c.md): SAML-Provider anlegen - [DELETE /v2/saml](/docs/api/v2/operations/delete-v2-saml-b41d9ec8c846.md): SAML-Provider entfernen - [GET /v2/saml/list](/docs/api/v2/operations/get-v2-saml-list-451b679b784c.md): SAML-Provider auflisten - [POST /v2/saml/list/no_data](/docs/api/v2/operations/post-v2-saml-list-no-data-66d2f2ee008c.md): Leeren SAML-Aktionszweig aufrufen - [GET /v2/saml/{id}](/docs/api/v2/operations/get-v2-saml-id-77451de13ed9.md): SAML-Provider lesen - [PUT /v2/saml/{id}](/docs/api/v2/operations/put-v2-saml-id-c26ca99b716d.md): SAML-Provider ändern - [DELETE /v2/saml/{id}](/docs/api/v2/operations/delete-v2-saml-id-8094e0d6e428.md): SAML-Provider entfernen - [GET /v2/saml/{id}/federationmetadata.xml](/docs/api/v2/operations/get-v2-saml-id-federationmetadata-xml-7ba89282ea00.md): SP-Metadaten für einen SAML-Provider herunterladen ### securefile - [GET /v2/securefile](/docs/api/v2/operations/get-v2-securefile-6fd102071b7d.md): Dateimetadaten auflisten - [PUT /v2/securefile](/docs/api/v2/operations/put-v2-securefile-72ce6fc662f2.md): Dateimetadaten ändern - [GET /v2/securefile/list](/docs/api/v2/operations/get-v2-securefile-list-ad3bab0c036c.md): Dateimetadaten auflisten - [GET /v2/securefile/{id}](/docs/api/v2/operations/get-v2-securefile-id-a09b48f44ef4.md): Dateimetadaten lesen - [PUT /v2/securefile/{id}](/docs/api/v2/operations/put-v2-securefile-id-98e0e3da983f.md): Dateimetadaten ändern ### sendemail - [POST /v2/sendemail](/docs/api/v2/operations/post-v2-sendemail-e7e1bb46534c.md): Nachricht an Teilnehmer, Angebote oder Administratoren senden ### settings - [GET /v2/settings](/docs/api/v2/operations/get-v2-settings-fec1627e5c2a.md): Account-Einstellungen abrufen - [PUT /v2/settings](/docs/api/v2/operations/put-v2-settings-92f4394b8018.md): Account-Einstellungen ändern - [GET /v2/settings/accesssystems](/docs/api/v2/operations/get-v2-settings-accesssystems-cd09fc40c281.md): Zugangssysteme und Prüfprotokoll abrufen - [PUT /v2/settings/accesssystems](/docs/api/v2/operations/put-v2-settings-accesssystems-5f10da2d05cc.md): Zugangssystem ändern oder Aktion ausführen - [POST /v2/settings/accesssystems](/docs/api/v2/operations/post-v2-settings-accesssystems-2567c4cbfe7e.md): Zugangssystem einrichten - [POST /v2/settings/legaltext](/docs/api/v2/operations/post-v2-settings-legaltext-b4940ece288b.md): Versionierbaren Rechtstext anlegen - [POST /v2/settings/legalversion](/docs/api/v2/operations/post-v2-settings-legalversion-909338d12448.md): Entwurf einer Rechtstextversion anlegen - [GET /v2/settings/list](/docs/api/v2/operations/get-v2-settings-list-b9082cd2c53c.md): Account-Einstellungen abrufen - [GET /v2/settings/list/DSG](/docs/api/v2/operations/get-v2-settings-list-dsg-d47d01790c3f.md): Gespeicherte DSG-Vertragsvorgänge abrufen - [POST /v2/settings/list/DSG](/docs/api/v2/operations/post-v2-settings-list-dsg-dc92edfb3463.md): DSG-Vertragsvorgang starten - [GET /v2/settings/list/GDPR](/docs/api/v2/operations/get-v2-settings-list-gdpr-b71abfeeaf69.md): Gespeicherte GDPR-Vertragsvorgänge abrufen - [POST /v2/settings/list/GDPR](/docs/api/v2/operations/post-v2-settings-list-gdpr-edef671c115a.md): GDPR-Vertragsvorgang starten - [GET /v2/settings/list/automatic_deletion](/docs/api/v2/operations/get-v2-settings-list-automatic-deletion-56416865449d.md): Vorschau der automatischen Löschung abrufen - [GET /v2/settings/list/dashboard](/docs/api/v2/operations/get-v2-settings-list-dashboard-e952be22c456.md): Startübersicht und Accountstatus abrufen - [GET /v2/settings/list/legal_text](/docs/api/v2/operations/get-v2-settings-list-legal-text-9f682d50ec80.md): Sprachabhängige Account-Rechtstexte abrufen - [PUT /v2/settings/list/legal_text](/docs/api/v2/operations/put-v2-settings-list-legal-text-b602cc927fe6.md): Sprachabhängigen Account-Rechtstext ändern - [POST /v2/settings/list/legal_text](/docs/api/v2/operations/post-v2-settings-list-legal-text-4a3f35bf570e.md): Sprachabhängigen Account-Rechtstext anlegen - [POST /v2/settings/list/legaltext](/docs/api/v2/operations/post-v2-settings-list-legaltext-02fcd660079d.md): Versionierbaren Rechtstext anlegen - [DELETE /v2/settings/list/legaltext](/docs/api/v2/operations/delete-v2-settings-list-legaltext-9c78a075a5f9.md): Versionierbaren Rechtstext löschen - [POST /v2/settings/list/legalversion](/docs/api/v2/operations/post-v2-settings-list-legalversion-4baec5d5376a.md): Entwurf einer Rechtstextversion anlegen - [GET /v2/settings/list/mailsystem](/docs/api/v2/operations/get-v2-settings-list-mailsystem-f0b490fd6303.md): Mailanbieter und Verbindungsstatus abrufen - [PUT /v2/settings/list/mailsystem](/docs/api/v2/operations/put-v2-settings-list-mailsystem-c1adf6a83ddd.md): Mailanbieter-Auswahl validieren - [PUT /v2/settings/list/mailsystem/edoobox-email](/docs/api/v2/operations/put-v2-settings-list-mailsystem-edoobox-email-fa659362c6a8.md): edoobox-Mailversand aktivieren - [PUT /v2/settings/list/mailsystem/mailjet](/docs/api/v2/operations/put-v2-settings-list-mailsystem-mailjet-57ddc9657487.md): Mailjet konfigurieren und prüfen - [PUT /v2/settings/list/mailsystem/microsoft](/docs/api/v2/operations/put-v2-settings-list-mailsystem-microsoft-5d683c5c1deb.md): Microsoft-Mailversand konfigurieren oder trennen - [PUT /v2/settings/list/mailsystem/smtp](/docs/api/v2/operations/put-v2-settings-list-mailsystem-smtp-ec8a6da70823.md): SMTP-Versand konfigurieren und prüfen - [GET /v2/settings/list/paper_sizes](/docs/api/v2/operations/get-v2-settings-list-paper-sizes-3e788b84e97a.md): Unterstützte PDF-Papiergrößen abrufen - [GET /v2/settings/list/timezones](/docs/api/v2/operations/get-v2-settings-list-timezones-70d4560fb736.md): Verfügbare Zeitzonen abrufen - [POST /v2/settings/text/legaltext](/docs/api/v2/operations/post-v2-settings-text-legaltext-9d374d33f791.md): Versionierbaren Rechtstext anlegen - [POST /v2/settings/text/legalversion](/docs/api/v2/operations/post-v2-settings-text-legalversion-bcc2d82b1026.md): Entwurf einer Rechtstextversion anlegen - [GET /v2/settings/{id}](/docs/api/v2/operations/get-v2-settings-id-46ae32622163.md): Account-Einstellungen abrufen - [PUT /v2/settings/{id}](/docs/api/v2/operations/put-v2-settings-id-2ec19893781e.md): Account-Einstellungen ändern - [PUT /v2/settings/{id}/GTM](/docs/api/v2/operations/put-v2-settings-id-gtm-fd5dd5fb66c1.md): Google-Tag-Manager-Kennungen speichern - [GET /v2/settings/{id}/dashboard](/docs/api/v2/operations/get-v2-settings-id-dashboard-ac00319fb9ca.md): Startübersicht und Accountstatus abrufen - [PUT /v2/settings/{id}/legal_text](/docs/api/v2/operations/put-v2-settings-id-legal-text-a59e29ec9992.md): Sprachabhängigen Account-Rechtstext ändern - [GET /v2/settings/{id}/legaltext](/docs/api/v2/operations/get-v2-settings-id-legaltext-cffe75eee2da.md): Versionierbaren Rechtstext abrufen - [PUT /v2/settings/{id}/legaltext](/docs/api/v2/operations/put-v2-settings-id-legaltext-efb243c5162b.md): Anzeigenamen eines versionierbaren Rechtstexts ändern - [DELETE /v2/settings/{id}/legaltext](/docs/api/v2/operations/delete-v2-settings-id-legaltext-24a82c0cc7d0.md): Versionierbaren Rechtstext löschen - [GET /v2/settings/{id}/legalversion](/docs/api/v2/operations/get-v2-settings-id-legalversion-5395cd54ac4a.md): Rechtstextversion abrufen - [PUT /v2/settings/{id}/legalversion](/docs/api/v2/operations/put-v2-settings-id-legalversion-09ab939448b7.md): Rechtstextentwurf bearbeiten oder fixieren - [PUT /v2/settings/{id}/mailsystem/edoobox-email](/docs/api/v2/operations/put-v2-settings-id-mailsystem-edoobox-email-869eb7a00652.md): edoobox-Mailversand aktivieren - [PUT /v2/settings/{id}/mailsystem/mailjet](/docs/api/v2/operations/put-v2-settings-id-mailsystem-mailjet-f22cf05c477a.md): Mailjet konfigurieren und prüfen - [PUT /v2/settings/{id}/mailsystem/microsoft](/docs/api/v2/operations/put-v2-settings-id-mailsystem-microsoft-47953ccd5e95.md): Microsoft-Mailversand konfigurieren oder trennen - [PUT /v2/settings/{id}/mailsystem/smtp](/docs/api/v2/operations/put-v2-settings-id-mailsystem-smtp-8c834f5fbae8.md): SMTP-Versand konfigurieren und prüfen ### support - [POST /v2/support/error-feedback-reports](/docs/api/v2/operations/post-v2-support-error-feedback-reports-473f173f35ee.md): Fehler- oder Ideenmeldung mit Anhängen speichern - [POST /v2/support/error-feedback-reports-placeholder](/docs/api/v2/operations/post-v2-support-error-feedback-reports-placeholder-e6f12c60c3f6.md): Fehler- oder Ideenmeldung mit Anhängen speichern - [POST /v2/support/error-feedback-reports-placeholder/finalize](/docs/api/v2/operations/post-v2-support-error-feedback-reports-placeholder-finalize-3bfef81dde96.md): Meldungsanhänge prüfen und abschließen - [POST /v2/support/error-feedback-reports-placeholder/prepare](/docs/api/v2/operations/post-v2-support-error-feedback-reports-placeholder-prepare-f227607603c6.md): Supportbericht und signierte Anhang-Uploads vorbereiten - [POST /v2/support/error-feedback-reports-placeholder/upload](/docs/api/v2/operations/post-v2-support-error-feedback-reports-placeholder-upload-68729f041cd3.md): Screenshot eines vorbereiteten Berichts hochladen - [POST /v2/support/error-feedback-reports/finalize](/docs/api/v2/operations/post-v2-support-error-feedback-reports-finalize-dcef4a7b2de9.md): Meldungsanhänge prüfen und abschließen - [POST /v2/support/error-feedback-reports/prepare](/docs/api/v2/operations/post-v2-support-error-feedback-reports-prepare-15ef627bb3f6.md): Supportbericht und signierte Anhang-Uploads vorbereiten - [POST /v2/support/error-feedback-reports/upload](/docs/api/v2/operations/post-v2-support-error-feedback-reports-upload-2fbc1167e033.md): Screenshot eines vorbereiteten Berichts hochladen ### tag - [GET /v2/tag](/docs/api/v2/operations/get-v2-tag-9da7a106d106.md): Angebots-Tag auflisten - [PUT /v2/tag](/docs/api/v2/operations/put-v2-tag-871841379c66.md): Angebots-Tag ändern - [POST /v2/tag](/docs/api/v2/operations/post-v2-tag-0a0a9d87ff8f.md): Angebots-Tag erstellen - [DELETE /v2/tag](/docs/api/v2/operations/delete-v2-tag-f06f0242979f.md): Angebots-Tag löschen - [GET /v2/tag/list](/docs/api/v2/operations/get-v2-tag-list-6f00b1635a0f.md): Angebots-Tag auflisten - [GET /v2/tag/list/connected_tags](/docs/api/v2/operations/get-v2-tag-list-connected-tags-3bc4b73d7d33.md): Verwendete Tags verbundener Konten lesen - [GET /v2/tag/{id}](/docs/api/v2/operations/get-v2-tag-id-1886482fa5da.md): Angebots-Tag lesen - [PUT /v2/tag/{id}](/docs/api/v2/operations/put-v2-tag-id-7a3ddbfcc951.md): Angebots-Tag ändern - [DELETE /v2/tag/{id}](/docs/api/v2/operations/delete-v2-tag-id-b79ea61c7847.md): Angebots-Tag löschen ### task - [GET /v2/task](/docs/api/v2/operations/get-v2-task-d2b755a5e689.md): Geplante Aufgaben auflisten - [PUT /v2/task](/docs/api/v2/operations/put-v2-task-7d5de58df8ae.md): Geplante aufgabe ändern - [POST /v2/task](/docs/api/v2/operations/post-v2-task-fcc01c322153.md): Geplante aufgabe anlegen - [DELETE /v2/task](/docs/api/v2/operations/delete-v2-task-77688f21e6e4.md): Geplante aufgabe entfernen - [GET /v2/task/list](/docs/api/v2/operations/get-v2-task-list-0b749460ae73.md): Geplante Aufgaben auflisten - [GET /v2/task/list/LUT](/docs/api/v2/operations/get-v2-task-list-lut-2665b2db9f99.md): Verfügbare Aufgabenfunktionen lesen - [GET /v2/task/{id}](/docs/api/v2/operations/get-v2-task-id-65a0258f2c61.md): Geplante aufgabe lesen - [PUT /v2/task/{id}](/docs/api/v2/operations/put-v2-task-id-f7b20cee86cd.md): Geplante aufgabe ändern - [DELETE /v2/task/{id}](/docs/api/v2/operations/delete-v2-task-id-1c9e0366fa8e.md): Geplante aufgabe entfernen ### template - [GET /v2/template](/docs/api/v2/operations/get-v2-template-bc05787cf104.md): Buchungsansicht für einen Website-Pfad rendern ### text - [GET /v2/text](/docs/api/v2/operations/get-v2-text-e76570e74051.md): Übersetzungstexte auflisten - [PUT /v2/text](/docs/api/v2/operations/put-v2-text-03a765c66122.md): Übersetzungstext ändern - [POST /v2/text](/docs/api/v2/operations/post-v2-text-aec1920c03c8.md): Übersetzungstext anlegen - [GET /v2/text/list](/docs/api/v2/operations/get-v2-text-list-3e39e93fd92b.md): Übersetzungstexte auflisten - [GET /v2/text/{id}](/docs/api/v2/operations/get-v2-text-id-805b5a77060c.md): Übersetzungstext lesen - [PUT /v2/text/{id}](/docs/api/v2/operations/put-v2-text-id-711d3f62eaff.md): Übersetzungstext ändern ### trainer-course - [GET /v2/auth/trainer-course](/docs/api/v2/operations/get-v2-auth-trainer-course-03830f9768a1.md): Formularauswahl und eigene Trainerangebote lesen - [POST /v2/auth/trainer-course](/docs/api/v2/operations/post-v2-auth-trainer-course-d982b815ab77.md): Trainer-Kursentwurf anlegen oder Datei hochladen ### transaction - [GET /v2/transaction](/docs/api/v2/operations/get-v2-transaction-cd253bcd2fc6.md): Teilnehmerkontobuchungen anzeigen - [POST /v2/transaction](/docs/api/v2/operations/post-v2-transaction-cda870a3b96a.md): Manuelle Teilnehmerkontobuchung anlegen - [POST /v2/transaction/file/camt](/docs/api/v2/operations/post-v2-transaction-file-camt-fd16390e2620.md): CAMT-Bankauszug importieren - [GET /v2/transaction/list](/docs/api/v2/operations/get-v2-transaction-list-52bfe786959f.md): Teilnehmerkontobuchungen anzeigen - [GET /v2/transaction/list/billable](/docs/api/v2/operations/get-v2-transaction-list-billable-04dcae8fe97b.md): Abrechenbare Teilnehmerpositionen ermitteln - [GET /v2/transaction/list/camt](/docs/api/v2/operations/get-v2-transaction-list-camt-c870c8ab8ae8.md): Importierte Bankeingänge anzeigen - [POST /v2/transaction/list/camt](/docs/api/v2/operations/post-v2-transaction-list-camt-68e5c7bf5cb6.md): CAMT-Bankauszug importieren - [GET /v2/transaction/{id}](/docs/api/v2/operations/get-v2-transaction-id-c6639ed86c0c.md): Teilnehmerkontobuchung prüfen - [PUT /v2/transaction/{id}/camt](/docs/api/v2/operations/put-v2-transaction-id-camt-5b6eb212b8c5.md): Bankeingang einer Rechnung zuordnen - [DELETE /v2/transaction/{id}/camt](/docs/api/v2/operations/delete-v2-transaction-id-camt-51961630c4b2.md): Unzugeordneten Bankeingang löschen - [GET /v2/transaction/{id}/pay](/docs/api/v2/operations/get-v2-transaction-id-pay-957203885f46.md): Zahlung und erstattbaren Restbetrag prüfen - [PUT /v2/transaction/{id}/refund](/docs/api/v2/operations/put-v2-transaction-id-refund-686ad3589b6d.md): Onlinezahlung teilweise oder vollständig erstatten ### user - [GET /v2/user](/docs/api/v2/operations/get-v2-user-3fbcf65c535d.md): Kundenstammdaten lesen - [PUT /v2/user](/docs/api/v2/operations/put-v2-user-7a0c2d49aa94.md): Kundenstammdaten aktualisieren - [POST /v2/user](/docs/api/v2/operations/post-v2-user-3593420b7d26.md): Kundenstammdatensatz erstellen - [DELETE /v2/user](/docs/api/v2/operations/delete-v2-user-7cc43f2519d8.md): Kunden ohne bestehende Buchungen löschen - [POST /v2/user/batch/batch_import](/docs/api/v2/operations/post-v2-user-batch-batch-import-905235419fff.md): Kunden und optional Buchungen importieren - [GET /v2/user/list](/docs/api/v2/operations/get-v2-user-list-d08972c27c71.md): Kundenstammdaten lesen - [POST /v2/user/list/I_want_demo_data](/docs/api/v2/operations/post-v2-user-list-i-want-demo-data-0dd7053821b5.md): Demo-Kunden für Entwicklung erzeugen - [POST /v2/user/list/files](/docs/api/v2/operations/post-v2-user-list-files-9fd7d2fac1be.md): Datei zu einer Ressource hochladen - [GET /v2/user/list/files/list](/docs/api/v2/operations/get-v2-user-list-files-list-78a2b2831800.md): Kundendateien und Dateimetadaten lesen - [GET /v2/user/list/files/{fileId}/download](/docs/api/v2/operations/get-v2-user-list-files-fileid-download-ee2a8993f235.md): Kundendatei herunterladen - [DELETE /v2/user/remove/files/{id}](/docs/api/v2/operations/delete-v2-user-remove-files-id-579871545d37.md): Datei als gelöscht markieren - [PUT /v2/user/update/files/{id}](/docs/api/v2/operations/put-v2-user-update-files-id-0ba6bc809bd4.md): Dateimetadaten aktualisieren - [POST /v2/user/upload/files](/docs/api/v2/operations/post-v2-user-upload-files-3bf78515f3e5.md): Datei zu einer Ressource hochladen - [GET /v2/user/{id}](/docs/api/v2/operations/get-v2-user-id-a89791558ba6.md): Kundenstammdaten lesen - [PUT /v2/user/{id}](/docs/api/v2/operations/put-v2-user-id-ddc272d8fe4a.md): Kundenstammdaten aktualisieren - [DELETE /v2/user/{id}](/docs/api/v2/operations/delete-v2-user-id-14f78e0a44f5.md): Kunden ohne bestehende Buchungen löschen - [POST /v2/user/{id}/batch_import](/docs/api/v2/operations/post-v2-user-id-batch-import-7039d95d6053.md): Kunden und optional Buchungen importieren - [GET /v2/user/{id}/dashboard](/docs/api/v2/operations/get-v2-user-id-dashboard-4a9fb74cd082.md): Kundenübersicht mit Buchungen und Salden laden - [GET /v2/user/{id}/files](/docs/api/v2/operations/get-v2-user-id-files-71d1f4841fc6.md): Kundendateien und Dateimetadaten lesen - [PUT /v2/user/{id}/files](/docs/api/v2/operations/put-v2-user-id-files-da6ade36121a.md): Dateimetadaten aktualisieren - [DELETE /v2/user/{id}/files](/docs/api/v2/operations/delete-v2-user-id-files-e753d1dc7200.md): Datei als gelöscht markieren - [PUT /v2/user/{id}/unnamed](/docs/api/v2/operations/put-v2-user-id-unnamed-f1d9419b60f5.md): Unbenannten Buchungsplatz mit Kundendaten aktivieren ### userduplicate - [POST /v2/userduplicate/clear](/docs/api/v2/operations/post-v2-userduplicate-clear-d3195e1363dc.md): Gefundene Duplikate und Suchindex leeren - [POST /v2/userduplicate/ignore](/docs/api/v2/operations/post-v2-userduplicate-ignore-c0bcbafb85b6.md): Kundenduplikat-Paar dauerhaft ignorieren - [POST /v2/userduplicate/merge](/docs/api/v2/operations/post-v2-userduplicate-merge-38ec466ddb45.md): Geprüfte Kundenduplikate zusammenführen - [GET /v2/userduplicate/open](/docs/api/v2/operations/get-v2-userduplicate-open-a999e229ae25.md): Offene Kundenduplikate zur Prüfung laden - [GET /v2/userduplicate/status](/docs/api/v2/operations/get-v2-userduplicate-status-89a541a6b17c.md): Stand der Kundenduplikatsuche lesen - [GET /v2/userduplicate/{id}/candidates](/docs/api/v2/operations/get-v2-userduplicate-id-candidates-2c8597c263e5.md): Duplikatkandidaten eines Kunden prüfen - [POST /v2/userduplicate/{id}/refresh](/docs/api/v2/operations/post-v2-userduplicate-id-refresh-8a6c0804caac.md): Duplikatsuche eines Kunden neu berechnen ### usermembership - [GET /v2/usermembership](/docs/api/v2/operations/get-v2-usermembership-f76ba4fb4018.md): Mitgliedschaften eines Kontos suchen - [PUT /v2/usermembership](/docs/api/v2/operations/put-v2-usermembership-10ae5ffc83f4.md): Administrative Mitgliedschaftsaktion ausführen - [POST /v2/usermembership](/docs/api/v2/operations/post-v2-usermembership-5979ce16c533.md): Mitgliedschaft für einen Benutzer kaufen - [GET /v2/usermembership/list](/docs/api/v2/operations/get-v2-usermembership-list-8137d816d8af.md): Mitgliedschaften eines Kontos suchen - [GET /v2/usermembership/list/invoice](/docs/api/v2/operations/get-v2-usermembership-list-invoice-3b28fa56ffcf.md): Rechnungslink einer Mitgliedschaft erzeugen - [GET /v2/usermembership/list/timeline](/docs/api/v2/operations/get-v2-usermembership-list-timeline-547b0cb474ca.md): Verlauf einer Mitgliedschaft lesen - [GET /v2/usermembership/{id}](/docs/api/v2/operations/get-v2-usermembership-id-b2edf8b8b55e.md): Mitgliedschaft mit Perioden und Kontingent lesen - [PUT /v2/usermembership/{id}](/docs/api/v2/operations/put-v2-usermembership-id-3c356521863b.md): Administrative Mitgliedschaftsaktion ausführen - [PUT /v2/usermembership/{id}/adjust-validity](/docs/api/v2/operations/put-v2-usermembership-id-adjust-validity-f4d52e881830.md): Enddatum einer Mitgliedschaft korrigieren - [POST /v2/usermembership/{id}/adjust-validity](/docs/api/v2/operations/post-v2-usermembership-id-adjust-validity-6b113b96cc69.md): Enddatum einer Mitgliedschaft korrigieren - [PUT /v2/usermembership/{id}/cancel](/docs/api/v2/operations/put-v2-usermembership-id-cancel-18d06ca1e608.md): Mitgliedschaft kündigen - [PUT /v2/usermembership/{id}/credit-uses](/docs/api/v2/operations/put-v2-usermembership-id-credit-uses-373a2fdc3da6.md): Nutzungskontingent gutschreiben - [POST /v2/usermembership/{id}/credit-uses](/docs/api/v2/operations/post-v2-usermembership-id-credit-uses-d71f15aeada7.md): Nutzungskontingent gutschreiben - [GET /v2/usermembership/{id}/invoice](/docs/api/v2/operations/get-v2-usermembership-id-invoice-51e78f6ad535.md): Rechnungslink einer Mitgliedschaft erzeugen - [PUT /v2/usermembership/{id}/payment-link](/docs/api/v2/operations/put-v2-usermembership-id-payment-link-f88b768b83cb.md): Zahlungslink für eine Mitgliedschaft erzeugen - [POST /v2/usermembership/{id}/payment-link](/docs/api/v2/operations/post-v2-usermembership-id-payment-link-9b7b770668d5.md): Zahlungslink für eine Mitgliedschaft erzeugen - [PUT /v2/usermembership/{id}/renewal](/docs/api/v2/operations/put-v2-usermembership-id-renewal-0df17534abeb.md): Automatische Verlängerung einstellen - [GET /v2/usermembership/{id}/timeline](/docs/api/v2/operations/get-v2-usermembership-id-timeline-64b93054a31d.md): Verlauf einer Mitgliedschaft lesen ### vat - [GET /v2/vat](/docs/api/v2/operations/get-v2-vat-b9e5e3957b76.md): Mehrwertsteuersätze anzeigen - [PUT /v2/vat](/docs/api/v2/operations/put-v2-vat-1e5d6c8a5849.md): Mehrwertsteuersatz aktualisieren - [POST /v2/vat](/docs/api/v2/operations/post-v2-vat-8efeed7736e8.md): Mehrwertsteuersatz anlegen - [GET /v2/vat/list](/docs/api/v2/operations/get-v2-vat-list-90d4aacd254c.md): Mehrwertsteuersätze anzeigen - [GET /v2/vat/{id}](/docs/api/v2/operations/get-v2-vat-id-33c434de3d80.md): Mehrwertsteuersatz prüfen - [PUT /v2/vat/{id}](/docs/api/v2/operations/put-v2-vat-id-e807c57f274a.md): Mehrwertsteuersatz aktualisieren ### webhook - [GET /v2/webhook](/docs/api/v2/operations/get-v2-webhook-55b2421efa1e.md): Webhook-Abonnements auflisten - [PUT /v2/webhook](/docs/api/v2/operations/put-v2-webhook-95bf97eeda14.md): Webhook-abonnement ändern - [POST /v2/webhook](/docs/api/v2/operations/post-v2-webhook-9ad5563f711d.md): Webhook-abonnement anlegen - [DELETE /v2/webhook](/docs/api/v2/operations/delete-v2-webhook-9aa4752d806a.md): Webhook-abonnement entfernen - [GET /v2/webhook/list](/docs/api/v2/operations/get-v2-webhook-list-8d6b9e105598.md): Webhook-Abonnements auflisten - [GET /v2/webhook/{id}](/docs/api/v2/operations/get-v2-webhook-id-e2a474c807cd.md): Webhook-abonnement lesen - [PUT /v2/webhook/{id}](/docs/api/v2/operations/put-v2-webhook-id-8276de4ad3cd.md): Webhook-abonnement ändern - [DELETE /v2/webhook/{id}](/docs/api/v2/operations/delete-v2-webhook-id-4e5c1b5c2519.md): Webhook-abonnement entfernen ### widget - [GET /v2/widget](/docs/api/v2/operations/get-v2-widget-3e68627caf54.md): Design-Widget auflisten - [PUT /v2/widget](/docs/api/v2/operations/put-v2-widget-73ff8799dbd7.md): Design-Widget ändern - [POST /v2/widget](/docs/api/v2/operations/post-v2-widget-d396f4144e58.md): Design-Widget anlegen - [DELETE /v2/widget](/docs/api/v2/operations/delete-v2-widget-8dbb9a84b39e.md): Design-Widget löschen - [GET /v2/widget/list](/docs/api/v2/operations/get-v2-widget-list-902a366ee427.md): Design-Widget auflisten - [GET /v2/widget/list/LUT](/docs/api/v2/operations/get-v2-widget-list-lut-369caa13f8fe.md): Verfügbare Widgettypen für eine Seitenvorlage lesen - [GET /v2/widget/{id}](/docs/api/v2/operations/get-v2-widget-id-bc52621aec74.md): Design-Widget lesen - [PUT /v2/widget/{id}](/docs/api/v2/operations/put-v2-widget-id-2887eb660c34.md): Design-Widget ändern - [DELETE /v2/widget/{id}](/docs/api/v2/operations/delete-v2-widget-id-ad9e269c8b79.md): Design-Widget löschen # POST /v2/accounting/bexio/authorize Bexio-Verbindung autorisieren Erstellt oder verwendet eine ausstehende Bexio-Verbindung und speichert einen einmaligen OAuth-Zustand mit PKCE und zehn Minuten Gültigkeit. Öffne authorization_url im Browser. Erfordert die Bexio-Freigabe und canConfigure. Optionales redirect_uri muss exakt der konfigurierten Callback-URI entsprechen. - Operation-ID: post_v2_accounting_bexio_authorize - Dokumentation: /docs/api/v2/operations/post-v2-accounting-bexio-authorize-0d5dbcf2856f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/accounting/bexio/authorize' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/bexio/authorize"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/bexio/authorize" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_accounting_bexio_authorize","summary":"Bexio-Verbindung autorisieren","description":"Erstellt oder verwendet eine ausstehende Bexio-Verbindung und speichert einen einmaligen OAuth-Zustand mit PKCE und zehn Minuten Gültigkeit. Öffne authorization_url im Browser. Erfordert die Bexio-Freigabe und canConfigure. Optionales redirect_uri muss exakt der konfigurierten Callback-URI entsprechen.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Autorisierungs-URL, einmaliger state, Ablauf als Unix-Sekunden und öffentliche connection_id.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"authorization_url":"https://idp.bexio.com/authorize?state=synthetic-state","state":"synthetic-state","expires_at":1792051800,"connection_id":"accountingconnection_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"authorization_url":{"type":"string"},"state":{"type":"string"},"expires_at":{"type":"integer"},"connection_id":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"redirect_uri":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Bexio-Verbindung autorisieren","value":{"data":{}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"postAccounting","line":149},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/accounting/bexio/callback Bexio-Browserrückkehr verarbeiten Öffentlicher OAuth-Callback für den Browser. Validiert den gespeicherten state, verarbeitet code oder error und aktiviert die Verbindung beziehungsweise verwirft den Zustand bei Abbruch/Fehler. Leitet mit result=success, cancelled oder failed zur konfigurierten Buchhaltungsoberfläche weiter; code und state werden nicht weitergereicht. Ungültige Frontend-Konfiguration kann vor einer Weiterleitung scheitern. - Operation-ID: get_v2_accounting_bexio_callback - Dokumentation: /docs/api/v2/operations/get-v2-accounting-bexio-callback-5ff04154c57c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | nein | | | state | query | ja | | | error | query | nein | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":false,"schema":{"type":"string","examples":["opaque-oauth-code"]}},{"name":"state","in":"query","required":true,"schema":{"type":"string","examples":["opaque-oauth-state"]}},{"name":"error","in":"query","required":false,"schema":{"type":"string","examples":["opaque-oauth-error"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_STATE:?QUERY_STATE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/accounting/bexio/callback' \ --url-query 'state='"${QUERY_STATE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/bexio/callback"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("state", required("QUERY_STATE")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/bexio/callback" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("state", os.environ["QUERY_STATE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_accounting_bexio_callback","summary":"Bexio-Browserrückkehr verarbeiten","description":"Öffentlicher OAuth-Callback für den Browser. Validiert den gespeicherten state, verarbeitet code oder error und aktiviert die Verbindung beziehungsweise verwirft den Zustand bei Abbruch/Fehler. Leitet mit result=success, cancelled oder failed zur konfigurierten Buchhaltungsoberfläche weiter; code und state werden nicht weitergereicht. Ungültige Frontend-Konfiguration kann vor einer Weiterleitung scheitern.","tags":["accounting"],"security":[],"parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","examples":["opaque-oauth-code"]}},{"name":"state","in":"query","required":true,"schema":{"type":"string","examples":["opaque-oauth-state"]}},{"name":"error","in":"query","required":false,"schema":{"type":"string","examples":["opaque-oauth-error"]}}],"responses":{"200":{"description":"HTTP 303 mit Location zur konfigurierten Oberfläche; vorhandene Fehlerantworten bleiben gültig."},"303":{"description":"When a valid accounting frontend base URI is configured, redirects to that configured UI result URL; OAuth code/state are not forwarded. This is a configuration-dependent browser response.","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}},"502":{"description":"Public server-error envelope when callback setup fails, including a missing or invalid accounting frontend base URI. Configuration is loaded before the callback local try/catch. No token exchange is required to reach this configuration failure; no configuration values or provider credentials are returned. Additional generic development error references may be present.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"const":false},"error":{"const":"ED50201"},"error_code":{"type":"string"}},"additionalProperties":true},"example":{"error":"ED50201","success":false}}}}},"x-edoobox-mutates-state":true,"x-edoobox-auth-boundarySource":{"file":"application/models/v2/Accounting.php","line":50,"reason":"Loads AccountingOAuthRedirect::fromEnvironment before the local try block; missing/invalid frontend configuration reaches the central ED50201 envelope. A configured redirect retains HTTP303."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"handlePublicAccountingOAuthCallback","line":485},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":false,"levels":[],"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme."}} ``` --- # POST /v2/accounting/bexio/callback Bexio-Autorisierung abschließen Tauscht den vom Anbieter zurückgegebenen code mit dem zuvor ausgestellten state aus, bindet die Verbindung an dasselbe Konto und speichert Tokens verschlüsselt. Erfordert gültigen, nicht verbrauchten Zustand und canConfigure; redirect_uri ist optional und muss bei Angabe exakt passen. - Operation-ID: post_v2_accounting_bexio_callback - Dokumentation: /docs/api/v2/operations/post-v2-accounting-bexio-callback-bd1d0ead32b5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "code": "synthetic-authorization-code", "state": "synthetic-state" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/accounting/bexio/callback' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/bexio/callback"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "code": "synthetic-authorization-code", "state": "synthetic-state" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/bexio/callback" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"code\":\"synthetic-authorization-code\",\"state\":\"synthetic-state\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_accounting_bexio_callback","summary":"Bexio-Autorisierung abschließen","description":"Tauscht den vom Anbieter zurückgegebenen code mit dem zuvor ausgestellten state aus, bindet die Verbindung an dasselbe Konto und speichert Tokens verschlüsselt. Erfordert gültigen, nicht verbrauchten Zustand und canConfigure; redirect_uri ist optional und muss bei Angabe exakt passen.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Objekt mit der autorisierten connection; keine Tokens.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"connection":{"id":"accountingconnection_012345abcdef_123456","provider":"bexio","status":"active","company":{"id":"example-company","name":"Beispielfirma"},"scopes":[]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"connection":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]},"provider":{"type":"string"},"status":{"type":"string"},"company":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"scopes":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"automatic_invoice":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"document_mode":{"type":"string","description":"Getrimmt und kleingeschrieben; draft, issue oder send."},"payment_sync_inbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"payment_sync_outbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"position_strategy":{"type":"string","description":"Getrimmt und kleingeschrieben; custom oder article."},"contact_field_mapping":{"type":"object","properties":{"salutation":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"first_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"last_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"street":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"house_number":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"address_addition":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"postal_code":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"city":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"country":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"email":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"phone":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"mobile":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"remarks":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]}},"additionalProperties":false},"send":{"type":"object","properties":{"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":20000},"mark_as_open":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."},"attach_pdf":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."}},"additionalProperties":true}},"additionalProperties":true},"connected_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"token_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_sync_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"health":{"type":"object","properties":{"last_check_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_error":{"type":"string"},"last_error_code":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"code":{"type":"string","minLength":1},"state":{"type":"string","minLength":1},"redirect_uri":{"type":"string"}},"additionalProperties":true,"required":["code","state"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Bexio-Autorisierung abschließen","value":{"data":{"code":"synthetic-authorization-code","state":"synthetic-state"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"postAccounting","line":149},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/accounting/connections Buchhaltungsverbindungen des Kontos anzeigen Liefert alle Buchhaltungsverbindungen mit Anbieter, Status, Firmenbezeichnung, normalisierten Einstellungen und bereinigten Gesundheitsmeldungen. Ohne Bexio-Freigabe ist connections leer; Tokens bleiben serverseitig. - Operation-ID: get_v2_accounting_connections - Dokumentation: /docs/api/v2/operations/get-v2-accounting-connections-35a813f0250f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_accounting_connections","summary":"Buchhaltungsverbindungen des Kontos anzeigen","description":"Liefert alle Buchhaltungsverbindungen mit Anbieter, Status, Firmenbezeichnung, normalisierten Einstellungen und bereinigten Gesundheitsmeldungen. Ohne Bexio-Freigabe ist connections leer; Tokens bleiben serverseitig.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Objekt mit connections-Liste.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"connections":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"connections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]},"provider":{"type":"string"},"status":{"type":"string"},"company":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"scopes":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"automatic_invoice":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"document_mode":{"type":"string","description":"Getrimmt und kleingeschrieben; draft, issue oder send."},"payment_sync_inbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"payment_sync_outbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"position_strategy":{"type":"string","description":"Getrimmt und kleingeschrieben; custom oder article."},"contact_field_mapping":{"type":"object","properties":{"salutation":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"first_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"last_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"street":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"house_number":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"address_addition":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"postal_code":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"city":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"country":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"email":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"phone":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"mobile":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"remarks":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]}},"additionalProperties":false},"send":{"type":"object","properties":{"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":20000},"mark_as_open":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."},"attach_pdf":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."}},"additionalProperties":true}},"additionalProperties":true},"connected_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"token_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_sync_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"health":{"type":"object","properties":{"last_check_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_error":{"type":"string"},"last_error_code":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/0","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Accounting.php","line":124,"lineEnd":146,"sha256":"c25cea041fb9c7d25c4a7a3ce96dc4a52e63dc670656ac8217388333d31ff161","reason":"GET accounting/connections is gated by Bexio rollout before getConnections; a disabled rollout returns an empty collection."},"reason":"Lists this account’s saved accounting connections; no provider synchronization or OAuth refresh is requested.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/0","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"getAccounting","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/accounting/connections/{connectionId} Buchhaltungsverbindung trennen Widerruft den entfernten Bexio-Refresh-Token und trennt die Verbindung des Kontos. Offene beziehungsweise unklare Buchhaltungsvorgänge können das Trennen blockieren; Fehler werden mit accounting_disconnect_blocked oder accounting_disconnect_failed gemeldet. - Operation-ID: delete_v2_accounting_connections_connectionId - Dokumentation: /docs/api/v2/operations/delete-v2-accounting-connections-connectionid-9088e03453ed/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_accounting_connections_connectionId","summary":"Buchhaltungsverbindung trennen","description":"Widerruft den entfernten Bexio-Refresh-Token und trennt die Verbindung des Kontos. Offene beziehungsweise unklare Buchhaltungsvorgänge können das Trennen blockieren; Fehler werden mit accounting_disconnect_blocked oder accounting_disconnect_failed gemeldet.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"delete=true und die öffentliche Verbindungs-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"delete":true,"id":"accountingconnection_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"delete":{"type":"boolean"},"id":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Buchhaltungsverbindung trennen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"deleteAccounting","line":258},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/accounting/connections/{connectionId} Buchhaltungsverbindung prüfen Liefert eine Buchhaltungsverbindung des aktuellen Kontos einschließlich Einstellungen, Synchronisationszeitpunkt und Gesundheitsstatus. - Operation-ID: get_v2_accounting_connections_connectionId - Dokumentation: /docs/api/v2/operations/get-v2-accounting-connections-connectionid-18a150b02235/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_accounting_connections_connectionId","summary":"Buchhaltungsverbindung prüfen","description":"Liefert eine Buchhaltungsverbindung des aktuellen Kontos einschließlich Einstellungen, Synchronisationszeitpunkt und Gesundheitsstatus.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Objekt mit connection.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"connection":{"id":"accountingconnection_012345abcdef_123456","provider":"bexio","status":"active","company":{"id":"example-company","name":"Beispielfirma"},"scopes":[]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"connection":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]},"provider":{"type":"string"},"status":{"type":"string"},"company":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"scopes":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"automatic_invoice":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"document_mode":{"type":"string","description":"Getrimmt und kleingeschrieben; draft, issue oder send."},"payment_sync_inbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"payment_sync_outbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"position_strategy":{"type":"string","description":"Getrimmt und kleingeschrieben; custom oder article."},"contact_field_mapping":{"type":"object","properties":{"salutation":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"first_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"last_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"street":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"house_number":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"address_addition":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"postal_code":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"city":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"country":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"email":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"phone":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"mobile":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"remarks":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]}},"additionalProperties":false},"send":{"type":"object","properties":{"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":20000},"mark_as_open":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."},"attach_pdf":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."}},"additionalProperties":true}},"additionalProperties":true},"connected_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"token_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_sync_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"health":{"type":"object","properties":{"last_check_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_error":{"type":"string"},"last_error_code":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/1","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Accounting.php","line":124,"lineEnd":146,"sha256":"c25cea041fb9c7d25c4a7a3ce96dc4a52e63dc670656ac8217388333d31ff161","reason":"Feature rollout gates the connections dispatcher."},"reason":"Reads a saved connection scoped to the current account.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/1","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"getAccounting","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/accounting/connections/{connectionId} Buchhaltungseinstellungen speichern Speichert die normalisierten Einstellungen einer Verbindung. Fehlende Einstellungen erhalten Standardwerte; ein fehlendes contact_field_mapping bewahrt die bisherige Kontaktzuordnung. document_mode akzeptiert draft/issue/send, position_strategy custom/article. Für send sind Betreff und eine Nachricht mit [Network Link] erforderlich. Die Aktivierung automatischer Rechnungen prüft notwendige Zuordnungen. Änderungen können fehlgeschlagene Rechnungsereignisse wieder öffnen und versenden; erfordert canConfigure. - Operation-ID: put_v2_accounting_connections_connectionId - Dokumentation: /docs/api/v2/operations/put-v2-accounting-connections-connectionid-70a9a4a151f8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "settings": { "automatic_invoice": false, "document_mode": "draft", "position_strategy": "custom" } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "settings": { "automatic_invoice": false, "document_mode": "draft", "position_strategy": "custom" } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"settings\":{\"automatic_invoice\":false,\"document_mode\":\"draft\",\"position_strategy\":\"custom\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_accounting_connections_connectionId","summary":"Buchhaltungseinstellungen speichern","description":"Speichert die normalisierten Einstellungen einer Verbindung. Fehlende Einstellungen erhalten Standardwerte; ein fehlendes contact_field_mapping bewahrt die bisherige Kontaktzuordnung. document_mode akzeptiert draft/issue/send, position_strategy custom/article. Für send sind Betreff und eine Nachricht mit [Network Link] erforderlich. Die Aktivierung automatischer Rechnungen prüft notwendige Zuordnungen. Änderungen können fehlgeschlagene Rechnungsereignisse wieder öffnen und versenden; erfordert canConfigure.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Objekt mit gespeicherter connection.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"connection":{"id":"accountingconnection_012345abcdef_123456","provider":"bexio","status":"active","company":{"id":"example-company","name":"Beispielfirma"},"scopes":[]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"connection":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]},"provider":{"type":"string"},"status":{"type":"string"},"company":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"scopes":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"automatic_invoice":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"document_mode":{"type":"string","description":"Getrimmt und kleingeschrieben; draft, issue oder send."},"payment_sync_inbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"payment_sync_outbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"position_strategy":{"type":"string","description":"Getrimmt und kleingeschrieben; custom oder article."},"contact_field_mapping":{"type":"object","properties":{"salutation":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"first_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"last_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"street":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"house_number":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"address_addition":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"postal_code":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"city":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"country":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"email":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"phone":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"mobile":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"remarks":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]}},"additionalProperties":false},"send":{"type":"object","properties":{"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":20000},"mark_as_open":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."},"attach_pdf":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."}},"additionalProperties":true}},"additionalProperties":true},"connected_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"token_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_sync_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"health":{"type":"object","properties":{"last_check_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_error":{"type":"string"},"last_error_code":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"settings":{"type":"object","properties":{"automatic_invoice":{"description":"PHP-Bool-Normalisierung; Standard false.","x-edoobox-canonicalType":"boolean"},"document_mode":{"type":"string","description":"Getrimmt und kleingeschrieben; draft, issue oder send."},"payment_sync_inbound":{"description":"PHP-Bool-Normalisierung; Standard false.","x-edoobox-canonicalType":"boolean"},"payment_sync_outbound":{"description":"PHP-Bool-Normalisierung; Standard false.","x-edoobox-canonicalType":"boolean"},"position_strategy":{"type":"string","description":"Getrimmt und kleingeschrieben; custom oder article."},"contact_field_mapping":{"type":"object","properties":{"salutation":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"first_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"last_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"street":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"house_number":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"address_addition":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"postal_code":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"city":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"country":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"email":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"phone":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"mobile":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"remarks":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]}},"additionalProperties":false},"send":{"type":"object","properties":{"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":20000},"mark_as_open":{"description":"PHP-Bool-Normalisierung; Standard true.","x-edoobox-canonicalType":"boolean"},"attach_pdf":{"description":"PHP-Bool-Normalisierung; Standard true.","x-edoobox-canonicalType":"boolean"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Buchhaltungseinstellungen speichern","value":{"data":{"settings":{"automatic_invoice":false,"document_mode":"draft","position_strategy":"custom"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"updateAccounting","line":184},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/accounting/connections/{connectionId}/issues Buchhaltungsprobleme prüfen Liefert bis zu 100 zusammengeführte und nach Änderungszeit absteigend sortierte Dokument-, Zahlungs- und Outbox-Probleme. available_actions beschreibt zulässige Schritte; bei inaktiver Verbindung ist es leer. Fehlertexte sind bereinigt. - Operation-ID: get_v2_accounting_connections_connectionId_issues - Dokumentation: /docs/api/v2/operations/get-v2-accounting-connections-connectionid-issues-17d9d5f00c86/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'/issues' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}/issues"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}/issues" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_accounting_connections_connectionId_issues","summary":"Buchhaltungsprobleme prüfen","description":"Liefert bis zu 100 zusammengeführte und nach Änderungszeit absteigend sortierte Dokument-, Zahlungs- und Outbox-Probleme. available_actions beschreibt zulässige Schritte; bei inaktiver Verbindung ist es leer. Fehlertexte sind bereinigt.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Objekt mit connection und issues; Zusatzfelder hängen von resource_type ab.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"connection":{"id":"accountingconnection_012345abcdef_123456","provider":"bexio","status":"active","company":{"id":"example-company","name":"Beispielfirma"},"scopes":[]},"issues":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"connection":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]},"provider":{"type":"string"},"status":{"type":"string"},"company":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"scopes":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"automatic_invoice":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"document_mode":{"type":"string","description":"Getrimmt und kleingeschrieben; draft, issue oder send."},"payment_sync_inbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"payment_sync_outbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"position_strategy":{"type":"string","description":"Getrimmt und kleingeschrieben; custom oder article."},"contact_field_mapping":{"type":"object","properties":{"salutation":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"first_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"last_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"street":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"house_number":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"address_addition":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"postal_code":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"city":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"country":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"email":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"phone":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"mobile":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"remarks":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]}},"additionalProperties":false},"send":{"type":"object","properties":{"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":20000},"mark_as_open":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."},"attach_pdf":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."}},"additionalProperties":true}},"additionalProperties":true},"connected_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"token_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_sync_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"health":{"type":"object","properties":{"last_check_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_error":{"type":"string"},"last_error_code":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true},"issues":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"resource_type":{"type":"string","enum":["document","payment","outbox"]},"resource_id":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"source":{"type":"object","properties":{"type":{"type":"string"},"id":{"type":"string"}},"additionalProperties":true},"external":{"type":"object","properties":{},"additionalProperties":true},"invoice":{"type":"object","properties":{},"additionalProperties":true},"payment":{"type":"object","properties":{},"additionalProperties":true},"event_type":{"type":"string"},"aggregate":{"type":"object","properties":{"type":{"type":"string"},"id":{"type":"string"}},"additionalProperties":true},"error":{"type":"object","properties":{"code":{"type":"string"},"has_private_details":{"type":"boolean"}},"additionalProperties":true},"available_actions":{"type":"array","items":{"type":"string"}},"requires_external_verification":{"type":"boolean"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}]}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/2","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Accounting.php","line":313,"lineEnd":339,"sha256":"c25cea041fb9c7d25c4a7a3ce96dc4a52e63dc670656ac8217388333d31ff161","reason":"Connection ownership is established before the issues branch."},"reason":"Reads stored document, payment-link and outbox issues; it does not reconcile or retry them.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/2","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"getAccounting","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/accounting/connections/{connectionId}/issues/reconcile Buchhaltungsproblem erneut abgleichen Startet für das angegebene Problem einer aktiven Verbindung einen zulässigen Zahlungsabgleich. resource_type ist document/payment/outbox, resource_id die dazugehörige öffentliche accountingdocument/accountingpayment/accountingoutbox-ID aus issues. Prüft die Auflösungsrichtlinie; unklare entfernte Schreibvorgänge werden nicht blind erneut gesendet. Dokument- und Zahlungsprobleme positionieren den Abgleichcursor; Outbox-Probleme öffnen einen fehlgeschlagenen Abgleich erneut. - Operation-ID: post_v2_accounting_connections_connectionId_issues_reconcile - Dokumentation: /docs/api/v2/operations/post-v2-accounting-connections-connectionid-issues-reconcile-7527a7b278ed/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "resource_type": "document", "resource_id": "accountingdocument_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'/issues/reconcile' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}/issues/reconcile"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "resource_type": "document", "resource_id": "accountingdocument_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}/issues/reconcile" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"resource_type\":\"document\",\"resource_id\":\"accountingdocument_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_accounting_connections_connectionId_issues_reconcile","summary":"Buchhaltungsproblem erneut abgleichen","description":"Startet für das angegebene Problem einer aktiven Verbindung einen zulässigen Zahlungsabgleich. resource_type ist document/payment/outbox, resource_id die dazugehörige öffentliche accountingdocument/accountingpayment/accountingoutbox-ID aus issues. Prüft die Auflösungsrichtlinie; unklare entfernte Schreibvorgänge werden nicht blind erneut gesendet. Dokument- und Zahlungsprobleme positionieren den Abgleichcursor; Outbox-Probleme öffnen einen fehlgeschlagenen Abgleich erneut.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"action=reconcile und run; queued bestätigt noch keinen erfolgreichen Abschluss.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"action":"reconcile","run":{"id":"accountingoutbox_012345abcdef_123456","status":"queued","direction":"both","type":"payments.reconcile","created_at":"2026-10-15T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"action":{"const":"reconcile"},"run":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingoutbox_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingoutbox_012345abcdef_123456"]},"status":{"const":"queued"},"direction":{"const":"both"},"type":{"const":"payments.reconcile"},"created_at":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"resource_type":{"type":"string","enum":["document","payment","outbox"]},"resource_id":{"type":"string"}},"additionalProperties":true,"required":["resource_type","resource_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Buchhaltungsproblem erneut abgleichen","value":{"data":{"resource_type":"document","resource_id":"accountingdocument_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"postAccounting","line":149},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/accounting/connections/{connectionId}/mappings Buchhaltungszuordnungen bearbeiten Liefert bis zu 500 lokale Zuordnungen mit Anbieteroptionen und required/automatic-Kennzeichen. Legt fehlende Standardzuordnungen an und kann bei Referenzabrufen OAuth-Tokens erneuern. Bexio-Artikelzuordnungen werden automatisch gepflegt; fehlende Referenzdaten verhindern die Ausgabe bestehender Zeilen nicht. - Operation-ID: get_v2_accounting_connections_connectionId_mappings - Dokumentation: /docs/api/v2/operations/get-v2-accounting-connections-connectionid-mappings-23e5500a6826/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'/mappings' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}/mappings"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}/mappings" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_accounting_connections_connectionId_mappings","summary":"Buchhaltungszuordnungen bearbeiten","description":"Liefert bis zu 500 lokale Zuordnungen mit Anbieteroptionen und required/automatic-Kennzeichen. Legt fehlende Standardzuordnungen an und kann bei Referenzabrufen OAuth-Tokens erneuern. Bexio-Artikelzuordnungen werden automatisch gepflegt; fehlende Referenzdaten verhindern die Ausgabe bestehender Zeilen nicht.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Objekt mit mappings-Liste und verfügbaren Optionen pro Zeile.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"mappings":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingmapping_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingmapping_012345abcdef_123456"]},"type":{"type":"string"},"type_name":{"type":"string"},"local_id":{"type":"string"},"local_name":{"type":"string"},"external_id":{"type":"string"},"external_name":{"type":"string"},"status":{"type":"string"},"required":{"type":"boolean"},"automatic":{"type":"boolean"},"scope":{"type":"string"},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"recommended":{"type":"boolean"}},"additionalProperties":true}}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Accounting.php","line":313,"lineEnd":323,"sha256":"c25cea041fb9c7d25c4a7a3ce96dc4a52e63dc670656ac8217388333d31ff161","reason":"After account-scoped connection lookup, mappings dispatches to mappings()."},"reason":"Reads mappings/reference options but first inserts or refreshes unmapped placeholders and may rotate provider credentials.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/3","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/3","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"getAccounting","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/accounting/connections/{connectionId}/mappings Buchhaltungszuordnungen speichern Aktualisiert jede übergebene Zuordnung derselben Verbindung anhand ihrer öffentlichen accountingmapping-ID. Leeres oder fehlendes external_id entfernt die externe Zuordnung; Bexio-Artikelzeilen werden übersprungen. Öffnet passende fehlgeschlagene Rechnungsereignisse erneut und kann diese unmittelbar versenden. Erfordert canConfigure. - Operation-ID: put_v2_accounting_connections_connectionId_mappings - Dokumentation: /docs/api/v2/operations/put-v2-accounting-connections-connectionid-mappings-03e163ea003b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "mappings": [ { "id": "accountingmapping_012345abcdef_123456", "external_id": "example-tax", "external_name": "Beispielsteuersatz" } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'/mappings' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}/mappings"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "mappings": [ { "id": "accountingmapping_012345abcdef_123456", "external_id": "example-tax", "external_name": "Beispielsteuersatz" } ] } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}/mappings" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"mappings\":[{\"id\":\"accountingmapping_012345abcdef_123456\",\"external_id\":\"example-tax\",\"external_name\":\"Beispielsteuersatz\"}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_accounting_connections_connectionId_mappings","summary":"Buchhaltungszuordnungen speichern","description":"Aktualisiert jede übergebene Zuordnung derselben Verbindung anhand ihrer öffentlichen accountingmapping-ID. Leeres oder fehlendes external_id entfernt die externe Zuordnung; Bexio-Artikelzeilen werden übersprungen. Öffnet passende fehlgeschlagene Rechnungsereignisse erneut und kann diese unmittelbar versenden. Erfordert canConfigure.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Aktuelle mappings-Liste nach dem Speichern.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"mappings":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingmapping_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingmapping_012345abcdef_123456"]},"type":{"type":"string"},"type_name":{"type":"string"},"local_id":{"type":"string"},"local_name":{"type":"string"},"external_id":{"type":"string"},"external_name":{"type":"string"},"status":{"type":"string"},"required":{"type":"boolean"},"automatic":{"type":"boolean"},"scope":{"type":"string"},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"recommended":{"type":"boolean"}},"additionalProperties":true}}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingmapping_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingmapping_012345abcdef_123456"]},"external_id":{"type":"string"},"external_name":{"type":"string"}},"additionalProperties":true,"required":["id"]}}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Buchhaltungszuordnungen speichern","value":{"data":{"mappings":[{"id":"accountingmapping_012345abcdef_123456","external_id":"example-tax","external_name":"Beispielsteuersatz"}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"updateAccounting","line":184},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/accounting/connections/{connectionId}/mappings/taxes Aktive Bexio-Steuern abrufen Liest alle aktiven Steueroptionen der aktiven Bexio-Verbindung, einschließlich recommended für Verkaufsbelege. Kann den OAuth-Token erneuern; inaktive Verbindung oder Anbieterfehler führen zu einer Fehlermeldung. - Operation-ID: get_v2_accounting_connections_connectionId_mappings_taxes - Dokumentation: /docs/api/v2/operations/get-v2-accounting-connections-connectionid-mappings-taxes-aee634d4465c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'/mappings/taxes' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}/mappings/taxes"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}/mappings/taxes" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_accounting_connections_connectionId_mappings_taxes","summary":"Aktive Bexio-Steuern abrufen","description":"Liest alle aktiven Steueroptionen der aktiven Bexio-Verbindung, einschließlich recommended für Verkaufsbelege. Kann den OAuth-Token erneuern; inaktive Verbindung oder Anbieterfehler führen zu einer Fehlermeldung.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Objekt mit options-Liste aus Anbieter-ID, Name und Empfehlung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"options":[{"id":"example-tax","name":"Beispielsteuersatz","recommended":true}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"recommended":{"type":"boolean"}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/4","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Accounting.php","line":313,"lineEnd":319,"sha256":"c25cea041fb9c7d25c4a7a3ce96dc4a52e63dc670656ac8217388333d31ff161","reason":"The exact mappings/taxes branch calls mappingOptions for tax with pagination; it does not call mappings() or seed placeholders."},"reason":"Fetches remote tax reference data; token expiry or a provider 401 can persist refreshed credentials.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/4","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"getAccounting","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/accounting/connections/{connectionId}/sync Synchronisationsverlauf prüfen Liefert die Verbindung und die letzten bis zu 100 protokollierten Synchronisationsläufe mit Zählern, Zeiten und bereinigtem Fehlertext. - Operation-ID: get_v2_accounting_connections_connectionId_sync - Dokumentation: /docs/api/v2/operations/get-v2-accounting-connections-connectionid-sync-5ed8fbf01893/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'/sync' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}/sync"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}/sync" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_accounting_connections_connectionId_sync","summary":"Synchronisationsverlauf prüfen","description":"Liefert die Verbindung und die letzten bis zu 100 protokollierten Synchronisationsläufe mit Zählern, Zeiten und bereinigtem Fehlertext.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Objekt mit connection und runs.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"connection":{"id":"accountingconnection_012345abcdef_123456","provider":"bexio","status":"active","company":{"id":"example-company","name":"Beispielfirma"},"scopes":[]},"runs":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"connection":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]},"provider":{"type":"string"},"status":{"type":"string"},"company":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"scopes":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","properties":{"automatic_invoice":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"document_mode":{"type":"string","description":"Getrimmt und kleingeschrieben; draft, issue oder send."},"payment_sync_inbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"payment_sync_outbound":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard false."},"position_strategy":{"type":"string","description":"Getrimmt und kleingeschrieben; custom oder article."},"contact_field_mapping":{"type":"object","properties":{"salutation":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"first_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"last_name":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"street":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"house_number":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"address_addition":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"postal_code":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"city":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"country":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"email":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"phone":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"mobile":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"remarks":{"type":"string","enum":["","gender","first_name","last_name","street","postcode","location","country","email","phone_number","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]}},"additionalProperties":false},"send":{"type":"object","properties":{"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":20000},"mark_as_open":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."},"attach_pdf":{"type":["boolean","string","number","null"],"description":"PHP-Bool-Normalisierung; Standard true."}},"additionalProperties":true}},"additionalProperties":true},"connected_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"token_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_sync_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"health":{"type":"object","properties":{"last_check_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"last_error":{"type":"string"},"last_error_code":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true},"runs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingsynclog_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingsynclog_012345abcdef_123456"]},"status":{"type":"string"},"direction":{"type":"string"},"type":{"type":"string"},"started_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"finished_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"processed":{"type":"integer"},"succeeded":{"type":"integer"},"failed":{"type":"integer"},"error":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/5","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Accounting.php","line":313,"lineEnd":329,"sha256":"c25cea041fb9c7d25c4a7a3ce96dc4a52e63dc670656ac8217388333d31ff161","reason":"Owned connection plus syncRuns are returned by GET; this branch does not call triggerSync."},"reason":"Reads the saved sync run history and connection status.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/5","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"getAccounting","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/accounting/connections/{connectionId}/sync Zahlungsabgleich starten Stellt für eine aktive Verbindung einen manuellen payments.reconcile-Auftrag in die Outbox und stößt deren Verarbeitung an. Wiederholte Aufrufe erzeugen weitere Aufträge; der zurückgegebene Status queued ist keine Bestätigung eines abgeschlossenen Abgleichs. - Operation-ID: post_v2_accounting_connections_connectionId_sync - Dokumentation: /docs/api/v2/operations/post-v2-accounting-connections-connectionid-sync-26f7bc909c01/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | connectionId | path | ja | Verbindungs-ID aus connections, im aktuellen Konto. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONNECTIONID:?PATH_CONNECTIONID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/accounting/connections/'"${PATH_CONNECTIONID}"'/sync' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/connections/{connectionId}/sync"; path = path.replaceAll("{connectionId}", encodeURIComponent(required("PATH_CONNECTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/connections/{connectionId}/sync" path = path.replace("{connectionId}", urllib.parse.quote(os.environ["PATH_CONNECTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_accounting_connections_connectionId_sync","summary":"Zahlungsabgleich starten","description":"Stellt für eine aktive Verbindung einen manuellen payments.reconcile-Auftrag in die Outbox und stößt deren Verarbeitung an. Wiederholte Aufrufe erzeugen weitere Aufträge; der zurückgegebene Status queued ist keine Bestätigung eines abgeschlossenen Abgleichs.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"connectionId","in":"path","required":true,"description":"Verbindungs-ID aus connections, im aktuellen Konto.","schema":{"type":"string","pattern":"^accountingconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingconnection_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Objekt mit run und öffentlicher Auftrags-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"run":{"id":"accountingoutbox_012345abcdef_123456","status":"queued","direction":"both","type":"payments.reconcile","created_at":"2026-10-15T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"run":{"type":"object","properties":{"id":{"type":"string","pattern":"^accountingoutbox_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["accountingoutbox_012345abcdef_123456"]},"status":{"const":"queued"},"direction":{"const":"both"},"type":{"const":"payments.reconcile"},"created_at":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Zahlungsabgleich starten","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"postAccounting","line":149},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/accounting/providers Verfügbare Buchhaltungsanbieter anzeigen Liefert die für dieses Konto freigeschalteten Anbieter mit Verfügbarkeit und unterstützten Fähigkeiten. Ohne Bexio-Freigabe ist providers leer. Es werden keine Zugangsdaten ausgegeben. - Operation-ID: get_v2_accounting_providers - Dokumentation: /docs/api/v2/operations/get-v2-accounting-providers-a7cad4e5f265/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/accounting/providers' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/accounting/providers"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/accounting/providers" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_accounting_providers","summary":"Verfügbare Buchhaltungsanbieter anzeigen","description":"Liefert die für dieses Konto freigeschalteten Anbieter mit Verfügbarkeit und unterstützten Fähigkeiten. Ohne Bexio-Freigabe ist providers leer. Es werden keine Zugangsdaten ausgegeben.","tags":["accounting"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Objekt mit providers-Liste.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"providers":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"description_text_code":{"type":"string"},"status":{"type":"string"},"available":{"type":"boolean"},"capabilities":{"type":"array","items":{"type":"string"}},"unsupported_capabilities":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/6","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Accounting.php","line":124,"lineEnd":132,"sha256":"c25cea041fb9c7d25c4a7a3ce96dc4a52e63dc670656ac8217388333d31ff161","reason":"providers GET selects providers(fid)."},"reason":"Returns provider metadata and availability flags from configuration and rollout checks.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/6","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Accounting.php","method":"getAccounting","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"accounting","short":"acco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/admin Administrator löschen Markiert das ausgewählte Administratorprofil als gelöscht, blockiert seine noch gültigen Anmeldungen und entfernt zugehörige SAML-Verknüpfungen. Die generische DELETE-Berechtigung und Accountzugehörigkeit werden geprüft. - Operation-ID: delete_v2_admin - Dokumentation: /docs/api/v2/operations/delete-v2-admin-283a5b07f571/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "admin_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/admin' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "admin_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"admin_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_admin","summary":"Administrator löschen","description":"Markiert das ausgewählte Administratorprofil als gelöscht, blockiert seine noch gültigen Anmeldungen und entfernt zugehörige SAML-Verknüpfungen. Die generische DELETE-Berechtigung und Accountzugehörigkeit werden geprüft.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Administrator-ID und delete-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"admin_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Administrator löschen","value":{"id":"admin_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"deleteAdmin","line":469},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin Administratorprofile abrufen Liest nicht gelöschte Administratorprofile des Accounts einschließlich Kontakt-, Sprach-, Berechtigungs- und Avatar-Metadaten. fields, filter und order wählen die ausgegebenen Profile und Felder; ein Pfadselektor wird als ID-Filter ergänzt. Passwörter werden nicht ausgegeben. - Operation-ID: get_v2_admin - Dokumentation: /docs/api/v2/operations/get-v2-admin-929fb6897432/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin","summary":"Administratorprofile abrufen","description":"Liest nicht gelöschte Administratorprofile des Accounts einschließlich Kontakt-, Sprach-, Berechtigungs- und Avatar-Metadaten. fields, filter und order wählen die ausgegebenen Profile und Felder; ein Pfadselektor wird als ID-Filter ergänzt. Passwörter werden nicht ausgegeben.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Profil-Sammlung in der generischen v2-Darstellung mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"admin_012345abcdef_123456":{"id":"admin_012345abcdef_123456","first_name":"Mara","last_name":"Muster","email":"mara@example.invalid","gender":"female","language":"de","avatar_key":"","avatar_version":""}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admin Administrator anlegen oder einladen Legt ein Administratorprofil im ausgewählten Account an; der aufrufende Administrator benötigt Admin-Schreibrecht. Bei einem einzelnen Profil steuert invite den Versand einer dreitägigen Einladung; dabei wird password ignoriert. Ohne invite kann ein geprüftes Passwort gesetzt werden. Ohne permission wird die Standardberechtigung des Accounts zugewiesen. Für einzelne Profile dient first_name zugleich als Erkennungsfeld des Einfügepfads; alternativ ist eine Liste von Datensätzen möglich. Einladung und Passwort werden nur beim einzelnen Profil ausgewertet. - Operation-ID: post_v2_admin - Dokumentation: /docs/api/v2/operations/post-v2-admin-3bd3b687be3b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "first_name": "Mara", "last_name": "Muster", "email": "mara@example.invalid", "invite": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admin' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "first_name": "Mara", "last_name": "Muster", "email": "mara@example.invalid", "invite": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"first_name\":\"Mara\",\"last_name\":\"Muster\",\"email\":\"mara@example.invalid\",\"invite\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admin","summary":"Administrator anlegen oder einladen","description":"Legt ein Administratorprofil im ausgewählten Account an; der aufrufende Administrator benötigt Admin-Schreibrecht. Bei einem einzelnen Profil steuert invite den Versand einer dreitägigen Einladung; dabei wird password ignoriert. Ohne invite kann ein geprüftes Passwort gesetzt werden. Ohne permission wird die Standardberechtigung des Accounts zugewiesen. Für einzelne Profile dient first_name zugleich als Erkennungsfeld des Einfügepfads; alternativ ist eine Liste von Datensätzen möglich. Einladung und Passwort werden nur beim einzelnen Profil ausgewertet.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche Administrator-ID oder bei Stapeln eine Liste von IDs; bei Einzel-Einladung zusätzlich invitation.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"admin_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"},"invitation":{"type":"object","properties":{"admin_id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"created_at":{"type":"integer"},"expires_at":{"type":"integer"},"status":{"const":"pending"},"mail_id":{"type":"boolean"}},"additionalProperties":true,"required":["admin_id","created_at","expires_at","status","mail_id"]}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":54,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"shortcut":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":30,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":63},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","examples":["female"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"academic_title":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Trim the PHP string-cast input; recognized academic titles are kept and every other value becomes the empty string. The title list is normalization, not a rejecting input enum. aP applies its normal text cleanup; 120 is a stored truncation bound, not a rejecting maxLength.","description":"Trim the PHP string-cast input; recognized academic titles are kept and every other value becomes the empty string. The title list is normalization, not a rejecting input enum. aP applies its normal text cleanup; 120 is a stored truncation bound, not a rejecting maxLength.","x-edoobox-recognizedValues":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"examples":[" Dr. "],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":93},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":102},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":111},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":120},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":129},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","examples":["de"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number_work":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":147},"phone_number_mobile":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":156},"birthdate":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":165},"email":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":174},"avatar_key":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."},"avatar_version":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."},"acl":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":207,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"permission":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Supported client value is an opaque permission v2 ID returned for the selected account. The legacy PUT hook uses edid(value, fid) without checkType; string shape alone does not establish that it is a permission record.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"rules":{"anyOf":[{"type":"object","properties":{"access":{"anyOf":[{"type":"object","properties":{"v1":{},"v2":{}},"additionalProperties":true},{"type":"array","items":{}}]}},"additionalProperties":true},{"type":["array","string","number","boolean","null"]}],"description":"Only access.v1/access.v2 are stored, each via PHP boolval. Unknown keys are ignored. A non-array top-level value stores empty JSON array; readback then uses defaults. access itself must decode to a PHP array when present.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"invite":{"type":["string","number","boolean","null","array","object"],"description":"Wird per FILTER_VALIDATE_BOOLEAN ausgewertet; true erstellt und versendet eine Einladung.","x-edoobox-canonicalType":"boolean"},"password":{"type":"string","writeOnly":true,"description":"Optionales Passwort; bei invite=true ignoriert."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"required":["first_name"]},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":54,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"shortcut":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":30,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":63},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","examples":["female"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"academic_title":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Trim the PHP string-cast input; recognized academic titles are kept and every other value becomes the empty string. The title list is normalization, not a rejecting input enum. aP applies its normal text cleanup; 120 is a stored truncation bound, not a rejecting maxLength.","description":"Trim the PHP string-cast input; recognized academic titles are kept and every other value becomes the empty string. The title list is normalization, not a rejecting input enum. aP applies its normal text cleanup; 120 is a stored truncation bound, not a rejecting maxLength.","x-edoobox-recognizedValues":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"examples":[" Dr. "],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":93},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":102},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":111},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":120},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":129},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","examples":["de"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number_work":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":147},"phone_number_mobile":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":156},"birthdate":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":165},"email":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":174},"avatar_key":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."},"avatar_version":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."},"acl":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":207,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"permission":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Supported client value is an opaque permission v2 ID returned for the selected account. The legacy PUT hook uses edid(value, fid) without checkType; string shape alone does not establish that it is a permission record.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"rules":{"anyOf":[{"type":"object","properties":{"access":{"anyOf":[{"type":"object","properties":{"v1":{},"v2":{}},"additionalProperties":true},{"type":"array","items":{}}]}},"additionalProperties":true},{"type":["array","string","number","boolean","null"]}],"description":"Only access.v1/access.v2 are stored, each via PHP boolval. Unknown keys are ignored. A non-array top-level value stores empty JSON array; readback then uses defaults. access itself must decode to a PHP array when present.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply."}}],"x-edoobox-readOnlyFields":["avatar_key","avatar_version"],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Administrator anlegen oder einladen","value":{"data":{"first_name":"Mara","last_name":"Muster","email":"mara@example.invalid","invite":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"postAdmin","line":303},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/admin Administratorprofil aktualisieren Ändert die übergebenen Profilfelder des ausgewählten Administrators. Das eigene Profil darf selbst bearbeitet werden; für andere Profile ist Admin-Schreibrecht erforderlich. permission und rules verlangen Superadmin-Recht, eigene Berechtigungen zusätzlich die Accountfreigabe. password ist ausschließlich beim eigenen Profil erlaubt und wird auf Passwortregeln geprüft. acl und Avatar-Metadaten sind nicht schreibbar. - Operation-ID: put_v2_admin - Dokumentation: /docs/api/v2/operations/put-v2-admin-eedad1b72f7a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "first_name": "Mara", "last_name": "Muster" }, "id": "admin_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/admin' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "first_name": "Mara", "last_name": "Muster" }, "id": "admin_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"first_name\":\"Mara\",\"last_name\":\"Muster\"},\"id\":\"admin_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_admin","summary":"Administratorprofil aktualisieren","description":"Ändert die übergebenen Profilfelder des ausgewählten Administrators. Das eigene Profil darf selbst bearbeitet werden; für andere Profile ist Admin-Schreibrecht erforderlich. permission und rules verlangen Superadmin-Recht, eigene Berechtigungen zusätzlich die Accountfreigabe. password ist ausschließlich beim eigenen Profil erlaubt und wird auf Passwortregeln geprüft. acl und Avatar-Metadaten sind nicht schreibbar.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Administrator-ID mit update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"admin_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":54,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"shortcut":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":30,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":63},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"academic_title":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Trim the PHP string-cast input; recognized academic titles are kept and every other value becomes the empty string. The title list is normalization, not a rejecting input enum. aP applies its normal text cleanup; 120 is a stored truncation bound, not a rejecting maxLength.","description":"Trim the PHP string-cast input; recognized academic titles are kept and every other value becomes the empty string. The title list is normalization, not a rejecting input enum. aP applies its normal text cleanup; 120 is a stored truncation bound, not a rejecting maxLength.","x-edoobox-recognizedValues":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":93},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":102},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":111},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":120},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":129},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number_work":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":147},"phone_number_mobile":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":156},"birthdate":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":165},"email":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":174},"avatar_key":false,"avatar_version":false,"acl":false,"permission":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Supported client value is an opaque permission v2 ID returned for the selected account. The legacy PUT hook uses edid(value, fid) without checkType; string shape alone does not establish that it is a permission record.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"rules":{"anyOf":[{"type":"object","properties":{"access":{"anyOf":[{"type":"object","properties":{"v1":{},"v2":{}},"additionalProperties":true},{"type":"array","items":{}}]}},"additionalProperties":true},{"type":["array","string","number","boolean","null"]}],"description":"Only access.v1/access.v2 are stored, each via PHP boolval. Unknown keys are ignored. A non-array top-level value stores empty JSON array; readback then uses defaults. access itself must decode to a PHP array when present.","examples":[{"access":{"v1":false,"v2":false}}],"x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"password":{"type":"string","writeOnly":true,"description":"Neues Passwort nur für das eigene Administratorprofil; die native Passwortprüfung bleibt maßgeblich."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["avatar_key","avatar_version","acl"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Administratorprofil aktualisieren","value":{"data":{"first_name":"Mara","last_name":"Muster"},"id":"admin_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"updateAdmin","line":386},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/admin/{id} Administrator löschen Markiert das ausgewählte Administratorprofil als gelöscht, blockiert seine noch gültigen Anmeldungen und entfernt zugehörige SAML-Verknüpfungen. Die generische DELETE-Berechtigung und Accountzugehörigkeit werden geprüft. - Operation-ID: delete_v2_admin_id - Dokumentation: /docs/api/v2/operations/delete-v2-admin-id-4bb8bdc480f1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_admin_id","summary":"Administrator löschen","description":"Markiert das ausgewählte Administratorprofil als gelöscht, blockiert seine noch gültigen Anmeldungen und entfernt zugehörige SAML-Verknüpfungen. Die generische DELETE-Berechtigung und Accountzugehörigkeit werden geprüft.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Administrator-ID und delete-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"admin_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Administrator löschen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"deleteAdmin","line":469},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/{id} Administratorprofile abrufen Liest nicht gelöschte Administratorprofile des Accounts einschließlich Kontakt-, Sprach-, Berechtigungs- und Avatar-Metadaten. fields, filter und order wählen die ausgegebenen Profile und Felder; ein Pfadselektor wird als ID-Filter ergänzt. Passwörter werden nicht ausgegeben. - Operation-ID: get_v2_admin_id - Dokumentation: /docs/api/v2/operations/get-v2-admin-id-b0497eccc41c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_id","summary":"Administratorprofile abrufen","description":"Liest nicht gelöschte Administratorprofile des Accounts einschließlich Kontakt-, Sprach-, Berechtigungs- und Avatar-Metadaten. fields, filter und order wählen die ausgegebenen Profile und Felder; ein Pfadselektor wird als ID-Filter ergänzt. Passwörter werden nicht ausgegeben.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Profil-Sammlung in der generischen v2-Darstellung mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"admin_012345abcdef_123456":{"id":"admin_012345abcdef_123456","first_name":"Mara","last_name":"Muster","email":"mara@example.invalid","gender":"female","language":"de","avatar_key":"","avatar_version":""}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/admin/{id} Administratorprofil aktualisieren Ändert die übergebenen Profilfelder des ausgewählten Administrators. Das eigene Profil darf selbst bearbeitet werden; für andere Profile ist Admin-Schreibrecht erforderlich. permission und rules verlangen Superadmin-Recht, eigene Berechtigungen zusätzlich die Accountfreigabe. password ist ausschließlich beim eigenen Profil erlaubt und wird auf Passwortregeln geprüft. acl und Avatar-Metadaten sind nicht schreibbar. - Operation-ID: put_v2_admin_id - Dokumentation: /docs/api/v2/operations/put-v2-admin-id-c7a854574876/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "first_name": "Mara", "last_name": "Muster" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "first_name": "Mara", "last_name": "Muster" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"first_name\":\"Mara\",\"last_name\":\"Muster\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_admin_id","summary":"Administratorprofil aktualisieren","description":"Ändert die übergebenen Profilfelder des ausgewählten Administrators. Das eigene Profil darf selbst bearbeitet werden; für andere Profile ist Admin-Schreibrecht erforderlich. permission und rules verlangen Superadmin-Recht, eigene Berechtigungen zusätzlich die Accountfreigabe. password ist ausschließlich beim eigenen Profil erlaubt und wird auf Passwortregeln geprüft. acl und Avatar-Metadaten sind nicht schreibbar.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Administrator-ID mit update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"admin_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":54,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"shortcut":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":30,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":63},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"academic_title":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Trim the PHP string-cast input; recognized academic titles are kept and every other value becomes the empty string. The title list is normalization, not a rejecting input enum. aP applies its normal text cleanup; 120 is a stored truncation bound, not a rejecting maxLength.","description":"Trim the PHP string-cast input; recognized academic titles are kept and every other value becomes the empty string. The title list is normalization, not a rejecting input enum. aP applies its normal text cleanup; 120 is a stored truncation bound, not a rejecting maxLength.","x-edoobox-recognizedValues":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":93},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":102},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":111},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":120},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":129},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number_work":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":147},"phone_number_mobile":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":156},"birthdate":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":165},"email":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":174},"avatar_key":false,"avatar_version":false,"acl":false,"permission":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Supported client value is an opaque permission v2 ID returned for the selected account. The legacy PUT hook uses edid(value, fid) without checkType; string shape alone does not establish that it is a permission record.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"rules":{"anyOf":[{"type":"object","properties":{"access":{"anyOf":[{"type":"object","properties":{"v1":{},"v2":{}},"additionalProperties":true},{"type":"array","items":{}}]}},"additionalProperties":true},{"type":["array","string","number","boolean","null"]}],"description":"Only access.v1/access.v2 are stored, each via PHP boolval. Unknown keys are ignored. A non-array top-level value stores empty JSON array; readback then uses defaults. access itself must decode to a PHP array when present.","examples":[{"access":{"v1":false,"v2":false}}],"x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"password":{"type":"string","writeOnly":true,"description":"Neues Passwort nur für das eigene Administratorprofil; die native Passwortprüfung bleibt maßgeblich."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["avatar_key","avatar_version","acl"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Administratorprofil aktualisieren","value":{"data":{"first_name":"Mara","last_name":"Muster"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"updateAdmin","line":386},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/{id}/course-creation-link Kurs-Erstellungslink eines Trainers abrufen Liest den vorhandenen geheimen Trainer-Token; ohne gespeicherten oder entschlüsselbaren Token wird eine leere Zeichenfolge geliefert. Dieser GET erstellt keinen Token. Der ausgewählte Administrator muss aktiv und als Trainer zugelassen sein; der Aufrufer benötigt die Berechtigung zur Linkverwaltung. Antwort vertraulich behandeln. - Operation-ID: get_v2_admin_id_course_creation_link - Dokumentation: /docs/api/v2/operations/get-v2-admin-id-course-creation-link-0ad3f71a05f6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/course-creation-link' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/course-creation-link"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/course-creation-link" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_id_course_creation_link","summary":"Kurs-Erstellungslink eines Trainers abrufen","description":"Liest den vorhandenen geheimen Trainer-Token; ohne gespeicherten oder entschlüsselbaren Token wird eine leere Zeichenfolge geliefert. Dieser GET erstellt keinen Token. Der ausgewählte Administrator muss aktiv und als Trainer zugelassen sein; der Aufrufer benötigt die Berechtigung zur Linkverwaltung. Antwort vertraulich behandeln.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"token und active. Ein leerer Token bedeutet, dass noch kein verwendbarer Link besteht.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"token":"","active":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"token":{"type":"string"},"active":{"const":true}},"additionalProperties":true,"required":["token","active"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitive-response-paths":["/data/token"],"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Trainercourse.php","line":11},"reason":"GET passes rotate=false and reads existing revocable tokens; sensitive output paths are independently redacted."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568,"branch":{"file":"application/models/v2/Admin.php","line":595}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admin/{id}/course-creation-link Kurs-Erstellungslink eines Trainers erneuern Erzeugt einen neuen geheimen Trainer-Token und ersetzt einen vorhandenen Token. Bereits verteilte Links mit altem Token werden ungültig. Der ausgewählte Administrator muss aktiv und als Trainer zugelassen sein; der Aufrufer benötigt die Berechtigung zur Linkverwaltung. Antwort vertraulich behandeln. - Operation-ID: post_v2_admin_id_course_creation_link - Dokumentation: /docs/api/v2/operations/post-v2-admin-id-course-creation-link-6b6f0b0f4d48/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/course-creation-link' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/course-creation-link"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/course-creation-link" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admin_id_course_creation_link","summary":"Kurs-Erstellungslink eines Trainers erneuern","description":"Erzeugt einen neuen geheimen Trainer-Token und ersetzt einen vorhandenen Token. Bereits verteilte Links mit altem Token werden ungültig. Der ausgewählte Administrator muss aktiv und als Trainer zugelassen sein; der Aufrufer benötigt die Berechtigung zur Linkverwaltung. Antwort vertraulich behandeln.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"token und active. Ein leerer Token bedeutet, dass noch kein verwendbarer Link besteht.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","active":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"token":{"type":"string"},"active":{"const":true}},"additionalProperties":true,"required":["token","active"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Kurs-Erstellungslink eines Trainers erneuern","value":{}}}}}},"x-edoobox-sensitive-response-paths":["/data/token"],"x-edoobox-user-handoff":{"sensitivePaths":["/data/token"],"oneTime":true},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"postAdmin","line":303,"branch":{"file":"application/models/v2/Admin.php","line":327}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/{id}/dashboard Administratorübersicht abrufen Liest das Profil des Administrators aus dem Pfad mit Einladungsstatus, SAML- und Zwei-Faktor-Status, Anzahl und Dauer seiner Termine, den zehn zeitlich nächsten Terminen und seinen letzten Anmeldungen. Zusätzliche Filter steuern diese Zusammenstellung nicht. - Operation-ID: get_v2_admin_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-admin-id-dashboard-b50cd1f026a6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_id_dashboard","summary":"Administratorübersicht abrufen","description":"Liest das Profil des Administrators aus dem Pfad mit Einladungsstatus, SAML- und Zwei-Faktor-Status, Anzahl und Dauer seiner Termine, den zehn zeitlich nächsten Terminen und seinen letzten Anmeldungen. Zusätzliche Filter steuern diese Zusammenstellung nicht.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Zusammengesetzte Administratorübersicht; verschachtelte Profil-, Auth- und Termindaten folgen deren Ressourcenverträgen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"admin":{"id":"admin_012345abcdef_123456","first_name":"Mara","last_name":"Muster"},"auth_sso":{"status":false},"auth_2fa":true,"admin_stats":{"count_date":4,"count_minutes":720},"auths":[],"closest_dates_10":[],"admin_id":"admin_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"admin":{"type":"object","properties":{},"additionalProperties":true},"auth_sso":{"type":"object","properties":{"status":{"type":"boolean"},"saml":{"type":"object","properties":{"id":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$"},"name":{"type":"string"},"acl":{"type":"boolean"}},"required":["id","name","acl"],"additionalProperties":true}},"required":["status"],"additionalProperties":true},"auth_2fa":{"type":"boolean"},"admin_stats":{"type":"object","properties":{"count_date":{"type":"number"},"count_minutes":{"type":"number"}},"additionalProperties":true},"auths":{},"closest_dates_10":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"admin_id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["admin","auth_sso","auth_2fa","admin_stats","auths","closest_dates_10","admin_id"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/dashboard/Admindashboard.php","line":9},"reason":"Complete reachable dashboard graph reviewed: SELECT aggregations, existing links, request-local formatting and serializers. Nested Eddata reads clear action segments and do not reach tree repair or other mutation branches. Derived read caches are not domain changes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568,"branch":{"file":"application/models/v2/Admin.php","line":585}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/admin/{id}/files Administratordatei entfernen Markiert die über ihre öffentliche files-ID ausgewählte Datei als gelöscht. Der übergeordnete Administrator wird dabei nicht gelöscht. Kundenspezifische Exportdateien sind über diese generische Ressource ausgeschlossen. - Operation-ID: delete_v2_admin_id_files - Dokumentation: /docs/api/v2/operations/delete-v2-admin-id-files-7855817b37a6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche files-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_admin_id_files","summary":"Administratordatei entfernen","description":"Markiert die über ihre öffentliche files-ID ausgewählte Datei als gelöscht. Der übergeordnete Administrator wird dabei nicht gelöscht. Kundenspezifische Exportdateien sind über diese generische Ressource ausgeschlossen.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Datei-ID und delete-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Administratordatei entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"deleteAdmin","line":469,"branch":{"file":"application/models/v2/Admin.php","line":493}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/{id}/files Dateimetadaten zu Administratoren abrufen Liest nicht gelöschte Dateimetadaten mit resource_type=admin. Über filter.resource_id kann ein Administrator ausgewählt werden. Ein Pfadselektor id bezeichnet hier die öffentliche files-ID und wird als Dateifilter verwendet. Kundenspezifische Exportdateien sind über diese generische Ressource ausgeschlossen. - Operation-ID: get_v2_admin_id_files - Dokumentation: /docs/api/v2/operations/get-v2-admin-id-files-3c73d5378a8e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Datei-ID; für den Eigentümer filter.resource_id verwenden. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Datei-ID; für den Eigentümer filter.resource_id verwenden.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_id_files","summary":"Dateimetadaten zu Administratoren abrufen","description":"Liest nicht gelöschte Dateimetadaten mit resource_type=admin. Über filter.resource_id kann ein Administrator ausgewählt werden. Ein Pfadselektor id bezeichnet hier die öffentliche files-ID und wird als Dateifilter verwendet. Kundenspezifische Exportdateien sind über diese generische Ressource ausgeschlossen.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Datei-ID; für den Eigentümer filter.resource_id verwenden.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Dateimetadaten in generischer v2-Sammlungsdarstellung; kein Dateiinhalt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"files_012345abcdef_123456":{"id":"files_012345abcdef_123456","filename":"unterschrift.png","filetype":"sign","resource_type":"admin","resource_id":"admin_012345abcdef_123456","description":{"de":"Unterschrift"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":true},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":true},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568,"branch":{"file":"application/models/v2/Admin.php","line":588}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/admin/{id}/files Metadaten einer Administratordatei ändern Ändert filename und sprachabhängige description einer vorhandenen Datei im Account. Der Pfad enthält die öffentliche files-ID; Dateiinhalt, Eigentümer und filetype werden mit diesem Metadaten-Update nicht ersetzt. Kundenspezifische Exportdateien sind ausgeschlossen. - Operation-ID: put_v2_admin_id_files - Dokumentation: /docs/api/v2/operations/put-v2-admin-id-files-5b31e0380755/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche files-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "filename": "unterschrift.png", "description": { "de": "Aktuelle Unterschrift" } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "filename": "unterschrift.png", "description": { "de": "Aktuelle Unterschrift" } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"filename\":\"unterschrift.png\",\"description\":{\"de\":\"Aktuelle Unterschrift\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_admin_id_files","summary":"Metadaten einer Administratordatei ändern","description":"Ändert filename und sprachabhängige description einer vorhandenen Datei im Account. Der Pfad enthält die öffentliche files-ID; Dateiinhalt, Eigentümer und filetype werden mit diesem Metadaten-Update nicht ersetzt. Kundenspezifische Exportdateien sind ausgeschlossen.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Datei-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"filename":{},"description":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Metadaten einer Administratordatei ändern","value":{"data":{"filename":"unterschrift.png","description":{"de":"Aktuelle Unterschrift"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"updateAdmin","line":386,"branch":{"file":"application/models/v2/Admin.php","line":398}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/admin/{id}/identity-provider SAML-Anmeldung eines Administrators lösen Entfernt genau eine eindeutige SAML-Verknüpfung dieses Administrators im Account. provider muss saml sein; OAuth-Anbieter werden abgelehnt. Verlangt Admin-DELETE-Recht oder Legacy-Superadmin. Das Profil und sein Passwort bleiben erhalten; bei keiner oder mehreren Verknüpfungen erfolgt ein Fehler. - Operation-ID: delete_v2_admin_id_identity_provider - Dokumentation: /docs/api/v2/operations/delete-v2-admin-id-identity-provider-8700e33abb1e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | | | language | header | nein | | | provider | query | nein | Alternativ zum JSON-Feld data.provider; nur saml wird unterstützt. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"schema":{"type":"string"}},{"in":"query","name":"provider","required":false,"description":"Alternativ zum JSON-Feld data.provider; nur saml wird unterstützt.","schema":{"type":"string","const":"saml"},"example":"saml"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "provider": "saml" } } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/identity-provider' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/identity-provider"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "provider": "saml" } }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/identity-provider" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"provider\":\"saml\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_admin_id_identity_provider","summary":"SAML-Anmeldung eines Administrators lösen","description":"Entfernt genau eine eindeutige SAML-Verknüpfung dieses Administrators im Account. provider muss saml sein; OAuth-Anbieter werden abgelehnt. Verlangt Admin-DELETE-Recht oder Legacy-Superadmin. Das Profil und sein Passwort bleiben erhalten; bei keiner oder mehreren Verknüpfungen erfolgt ein Fehler.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"schema":{"type":"string"}},{"in":"query","name":"provider","required":false,"description":"Alternativ zum JSON-Feld data.provider; nur saml wird unterstützt.","schema":{"type":"string","const":"saml"},"example":"saml"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Administrator-ID und zurückgesetzter Verknüpfungsstatus.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"admin_012345abcdef_123456","auth_connection":{"provider":"password","active":false,"password_locked":false,"connection_id":false,"acl":false}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"auth_connection":{"type":"object","properties":{"provider":{"const":"password"},"active":{"const":false},"password_locked":{"const":false},"connection_id":{"const":false},"acl":{"const":false}},"additionalProperties":true}},"additionalProperties":true,"required":["id","auth_connection"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"provider":{"const":"saml"}},"required":["provider"],"additionalProperties":true},"provider":{"const":"saml"}},"anyOf":[{"required":["data"]},{"required":["provider"]}],"additionalProperties":true},"examples":{"request":{"summary":"SAML-Anmeldung eines Administrators lösen","value":{"data":{"provider":"saml"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"deleteAdmin","line":469},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admin/{id}/invitation Administratoreinladung erneut senden Widerruft offene Einladungen für den ausgewählten Administrator und versendet eine neue Einladung, die drei Tage gültig ist. Benötigt Admin-Schreibrecht sowie einen vorhandenen Empfänger und Absender im Account; der Aufruf ist kein Konto-Signup. - Operation-ID: post_v2_admin_id_invitation - Dokumentation: /docs/api/v2/operations/post-v2-admin-id-invitation-ce892c322616/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/invitation' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/invitation"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/invitation" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admin_id_invitation","summary":"Administratoreinladung erneut senden","description":"Widerruft offene Einladungen für den ausgewählten Administrator und versendet eine neue Einladung, die drei Tage gültig ist. Benötigt Admin-Schreibrecht sowie einen vorhandenen Empfänger und Absender im Account; der Aufruf ist kein Konto-Signup.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Einladungsstatus und Unix-Zeitpunkte; mail_id ist ein Versand-Erfolgsschalter, keine Datenbank-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"invitation":{"admin_id":"admin_012345abcdef_123456","created_at":1792620000,"expires_at":1792879200,"status":"pending","mail_id":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"invitation":{"type":"object","properties":{"admin_id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"created_at":{"type":"integer"},"expires_at":{"type":"integer"},"status":{"const":"pending"},"mail_id":{"type":"boolean"}},"additionalProperties":true,"required":["admin_id","created_at","expires_at","status","mail_id"]}},"additionalProperties":true,"required":["invitation"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Administratoreinladung erneut senden","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"postAdmin","line":303,"branch":{"file":"application/models/v2/Admin.php","line":317}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/{id}/workload Auslastung eines Administrators abrufen Liest die zugewiesenen Kurstermine eines Administrators im Zeitraum, einschließlich Angebot, Termin, Ort, Raum und Status. Die JSON-Filter start und end sind erforderlich und werden als Datumswerte verarbeitet. Der Pfad liefert den ersten ID-Filter; zusätzliche start/end-Filter bleiben erforderlich. - Operation-ID: get_v2_admin_id_workload - Dokumentation: /docs/api/v2/operations/get-v2-admin-id-workload-e362decfe986/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter start/end; bei /list muss der erste Filter id sein. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter start/end; bei /list muss der erste Filter id sein.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/'"${PATH_ID}"'/workload' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/{id}/workload"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/{id}/workload" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_id_workload","summary":"Auslastung eines Administrators abrufen","description":"Liest die zugewiesenen Kurstermine eines Administrators im Zeitraum, einschließlich Angebot, Termin, Ort, Raum und Status. Die JSON-Filter start und end sind erforderlich und werden als Datumswerte verarbeitet. Der Pfad liefert den ersten ID-Filter; zusätzliche start/end-Filter bleiben erforderlich.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter start/end; bei /list muss der erste Filter id sein.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"Spaltendefinitionen mit positionsgleichen Zeilen; limit enthält die Paginierung. Felder/Sortierung/response_type werden für diese Tabelle nicht verwendet.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"offername","type":"string"},{"field":"name","type":"string"},{"field":"offer_id","type":"string"},{"field":"date_id","type":"string"},{"field":"offerdef_id","type":"string"},{"field":"place_id","type":"string"},{"field":"room_id","type":"string"},{"field":"status","type":"integer"},{"field":"date_start","type":"date"},{"field":"date_end","type":"date"}],"rows":[["Beispielkurs","Vormittag","offer_012345abcdef_123456","date_012345abcdef_123456","offerdef_012345abcdef_123456","place_012345abcdef_123456","room_012345abcdef_123456",1,"2026-11-03T09:00:00+01:00","2026-11-03T12:00:00+01:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Admin.php","line":511,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminfunc.php","line":172},"reason":"Workload SELECT and formatting only; Placefunc194/Roomfunc12 likewise."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568,"branch":{"file":"application/models/v2/Admin.php","line":581}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/list Administratorprofile abrufen Liest nicht gelöschte Administratorprofile des Accounts einschließlich Kontakt-, Sprach-, Berechtigungs- und Avatar-Metadaten. fields, filter und order wählen die ausgegebenen Profile und Felder; ein Pfadselektor wird als ID-Filter ergänzt. Passwörter werden nicht ausgegeben. - Operation-ID: get_v2_admin_list - Dokumentation: /docs/api/v2/operations/get-v2-admin-list-ac87ba46a850/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_list","summary":"Administratorprofile abrufen","description":"Liest nicht gelöschte Administratorprofile des Accounts einschließlich Kontakt-, Sprach-, Berechtigungs- und Avatar-Metadaten. fields, filter und order wählen die ausgegebenen Profile und Felder; ein Pfadselektor wird als ID-Filter ergänzt. Passwörter werden nicht ausgegeben.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Profil-Sammlung in der generischen v2-Darstellung mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"admin_012345abcdef_123456":{"id":"admin_012345abcdef_123456","first_name":"Mara","last_name":"Muster","email":"mara@example.invalid","gender":"female","language":"de","avatar_key":"","avatar_version":""}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":54,"type":"boolean"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":72,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"academic_title":{"type":"string","enum":["","Dr.","Dr. med.","Dr. phil.","Prof.","Prof. Dr.","Prof. Dr. med.","PD Dr.","PD Dr. med."],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":82,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":23,"reason":"Recognized title values used for normalization and response fallback.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":111,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":129,"type":"string"},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":138,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number_work":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"type":"string"},"phone_number_mobile":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":156,"type":"string"},"birthdate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":165,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":174},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":183,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":195,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":207,"type":"integer"},"permission":{"type":"string","description":"Opaque v2 permission ID produced by edid(..., permission, true).","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":222,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Adminfunc.php","line":66,"reason":"Resource-specific permission update lookup and ACL/custom-permission gates.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Adminfunc.php","line":6,"reason":"Before-insert hook uses normalized permission or resolves account default.","sha256":"d5380c3bd75ef781d871ee0bb5c0be2c220974c4935abdb225f0a535fed210d8"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Admin permission/rules changes pass the explicit admin authorization checks before generic update.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"rules":{"type":"object","properties":{"access":{"type":"object","properties":{"v1":{},"v2":{}},"required":["v1","v2"],"additionalProperties":false}},"required":["access"],"additionalProperties":false,"description":"Normally booleans after this writer. Legacy stored JSON values are copied without output bool conversion; unknown/missing keys use true defaults.","x-edoobox-contractSource":[{"file":"application/models/v2/Admin.php","line":232,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":17,"reason":"Default recognized groups and true access values.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Review of update authorization and dispatch; no permissions were changed.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":303,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":355,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":386,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"application/models/v2/Admin.php","line":431,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"5e35918c27ac3792f750d428559354081bec35b67dc432a5c4da78e2e2f141bd"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/list/course-creation-links Kurs-Erstellungslinks aller Trainer abrufen Listet aktive, nicht gelöschte Administratoren mit zulässiger Trainerberechtigung und ihren vorhandenen geheimen Tokens, sortiert nach Nachname und Vorname. Fehlende Tokens bleiben leer; der GET legt keine Links an. Der Aufrufer benötigt Linkverwaltungsrecht; Antwort vertraulich behandeln. - Operation-ID: get_v2_admin_list_course_creation_links - Dokumentation: /docs/api/v2/operations/get-v2-admin-list-course-creation-links-118b06150833/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/list/course-creation-links' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/list/course-creation-links"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/list/course-creation-links" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_list_course_creation_links","summary":"Kurs-Erstellungslinks aller Trainer abrufen","description":"Listet aktive, nicht gelöschte Administratoren mit zulässiger Trainerberechtigung und ihren vorhandenen geheimen Tokens, sortiert nach Nachname und Vorname. Fehlende Tokens bleiben leer; der GET legt keine Links an. Der Aufrufer benötigt Linkverwaltungsrecht; Antwort vertraulich behandeln.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Liste mit öffentlicher Administrator-ID, Anzeigename und geheimem Token.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"admin_012345abcdef_123456","name":"Mara Muster","token":""}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"},"token":{"type":"string"}},"additionalProperties":true,"required":["id","name","token"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitive-response-paths":["/data/*/token"],"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Trainercourse.php","line":11},"reason":"GET passes rotate=false and reads existing revocable tokens; sensitive output paths are independently redacted."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568,"branch":{"file":"application/models/v2/Admin.php","line":599}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admin/list/customized_data Kundenspezifischen Administrator-Export starten Startet den für den Account freigegebenen kundenspezifischen Export. task wählt LuksExport1 (Themenblöcke) oder LuksExport2 (Themenblöcke mit Administratoren); der Taskname wird auf Buchstaben/Ziffern normalisiert. date_start und date_end müssen in Datumswerte umwandelbar sein. Diese Tasks sind nur für ausdrücklich freigeschaltete Accounts vorhanden; nach der Bestätigung wird der Export weiterverarbeitet. - Operation-ID: post_v2_admin_list_customized_data - Dokumentation: /docs/api/v2/operations/post-v2-admin-list-customized-data-bf7f39a8586f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "task": "LuksExport2", "date_start": "2026-11-01T00:00:00+01:00", "date_end": "2026-12-01T00:00:00+01:00" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admin/list/customized_data' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/list/customized_data"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "task": "LuksExport2", "date_start": "2026-11-01T00:00:00+01:00", "date_end": "2026-12-01T00:00:00+01:00" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/list/customized_data" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"task\":\"LuksExport2\",\"date_start\":\"2026-11-01T00:00:00+01:00\",\"date_end\":\"2026-12-01T00:00:00+01:00\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admin_list_customized_data","summary":"Kundenspezifischen Administrator-Export starten","description":"Startet den für den Account freigegebenen kundenspezifischen Export. task wählt LuksExport1 (Themenblöcke) oder LuksExport2 (Themenblöcke mit Administratoren); der Taskname wird auf Buchstaben/Ziffern normalisiert. date_start und date_end müssen in Datumswerte umwandelbar sein. Diese Tasks sind nur für ausdrücklich freigeschaltete Accounts vorhanden; nach der Bestätigung wird der Export weiterverarbeitet.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bestätigt den Exportstart mit export=pending; enthält noch keine Exportdatei.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":false,"export":"pending"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"const":false},"export":{"const":"pending"}},"additionalProperties":true,"required":["insert","export"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"task":{"type":"string","description":"Implementierte Tasks: LuksExport1 und LuksExport2; nur für freigeschaltete Accounts."},"date_start":{"type":["string","number","boolean","null"]},"date_end":{"type":["string","number","boolean","null"]}},"additionalProperties":true,"required":["task","date_start","date_end"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kundenspezifischen Administrator-Export starten","value":{"data":{"task":"LuksExport2","date_start":"2026-11-01T00:00:00+01:00","date_end":"2026-12-01T00:00:00+01:00"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"postAdmin","line":303,"branch":{"file":"application/models/v2/Admin.php","line":311}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admin/list/files Datei zu einem Administrator hochladen Lädt eine Datei hoch und verknüpft sie mit dem über resource_id ausgewählten Datensatz. resource_type und filetype müssen im Metadatenobjekt gesetzt sein; für Administratoren üblicherweise admin mit sign oder avatar. Es gelten die Owner-Prüfung, MIME-/Größenprüfung sowie bei Admin-Avataren gegebenenfalls die Gesichtserkennung. Signaturen und Avatare ersetzen vorhandene Dateien desselben Typs durch Soft-Löschung. - Operation-ID: post_v2_admin_list_files - Dokumentation: /docs/api/v2/operations/post-v2-admin-list-files-b221ae1eefd8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admin/list/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/list/files"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/admin/list/files" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admin_list_files","summary":"Datei zu einem Administrator hochladen","description":"Lädt eine Datei hoch und verknüpft sie mit dem über resource_id ausgewählten Datensatz. resource_type und filetype müssen im Metadatenobjekt gesetzt sein; für Administratoren üblicherweise admin mit sign oder avatar. Es gelten die Owner-Prüfung, MIME-/Größenprüfung sowie bei Admin-Avataren gegebenenfalls die Gesichtserkennung. Signaturen und Avatare ersetzen vorhandene Dateien desselben Typs durch Soft-Löschung.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche ID der neu gespeicherten Datei und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Dateibytes. Avatare: JPEG/PNG/WebP, höchstens 8 MiB; andere Bilder höchstens 2 MiB, Angebotsanhänge höchstens 20 MiB."},"meta":{"type":"string","description":"JSON-Zeichenfolge mit dem Envelope {data:{resource_id,resource_type,filetype,...}}; alternativ direkte Multipart-Formfelder.","examples":["{\"data\":{\"resource_id\":\"admin_012345abcdef_123456\",\"filename\":\"unterschrift.png\",\"resource_type\":\"admin\",\"filetype\":\"sign\"}}"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$"},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$"},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$"}],"description":"Öffentliche Eigentümer-ID bei direkten Multipart-Feldern."},"filename":{"type":"string"},"resource_type":{"enum":["admin","user","offer"]},"filetype":{"enum":["avatar","sign","image2","image3","internalOfferFile","externalOfferFile"]}},"required":["file"],"anyOf":[{"required":["meta"]},{"required":["resource_id","resource_type","filetype"]}],"additionalProperties":true},"examples":{"upload":{"summary":"Synthetische Metadaten; file durch echte Dateibytes ersetzen.","value":{"file":"","meta":"{\"data\":{\"resource_id\":\"admin_012345abcdef_123456\",\"filename\":\"unterschrift.png\",\"resource_type\":\"admin\",\"filetype\":\"sign\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"postAdmin","line":303,"branch":{"file":"application/models/v2/Admin.php","line":314}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/list/files/{fileId}/download Avatar- oder Administratordatei herunterladen Liefert die Datei über ihre öffentliche files-ID als Download, falls sie im Account vorhanden und nicht gelöscht ist. Diese Downloadfunktion prüft die Datei-ID und Accountzugehörigkeit; die Avatar-Filter des Aufrufers beschränken den direkten Downloadpfad nicht zusätzlich. Kundenspezifische Exportdateien bleiben ausgeschlossen; fehlende Dateien liefern einen JSON-Fehler. - Operation-ID: get_v2_admin_list_files_fileId_download - Dokumentation: /docs/api/v2/operations/get-v2-admin-list-files-fileid-download-53117622f756/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fileId | path | ja | Öffentliche files-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fileId","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_FILEID:?PATH_FILEID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/list/files/'"${PATH_FILEID}"'/download' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/list/files/{fileId}/download"; path = path.replaceAll("{fileId}", encodeURIComponent(required("PATH_FILEID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/list/files/{fileId}/download" path = path.replace("{fileId}", urllib.parse.quote(os.environ["PATH_FILEID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_list_files_fileId_download","summary":"Avatar- oder Administratordatei herunterladen","description":"Liefert die Datei über ihre öffentliche files-ID als Download, falls sie im Account vorhanden und nicht gelöscht ist. Diese Downloadfunktion prüft die Datei-ID und Accountzugehörigkeit; die Avatar-Filter des Aufrufers beschränken den direkten Downloadpfad nicht zusätzlich. Kundenspezifische Exportdateien bleiben ausgeschlossen; fehlende Dateien liefern einen JSON-Fehler.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fileId","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Binärer Download mit Dateiname; bei fehlender Datei JSON-Fehler ED80100/file_not_found.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_code":{"type":"string"},"error_message":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true},"example":{"error":"ED80100","error_code":"file_not_found","error_message":"The requested file could not be found or has been deleted","type":"api"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/list/files/list Dateimetadaten zu Administratoren abrufen Liest nicht gelöschte Dateimetadaten mit resource_type=admin. Über filter.resource_id kann ein Administrator ausgewählt werden. Ein Pfadselektor id bezeichnet hier die öffentliche files-ID und wird als Dateifilter verwendet. Kundenspezifische Exportdateien sind über diese generische Ressource ausgeschlossen. - Operation-ID: get_v2_admin_list_files_list - Dokumentation: /docs/api/v2/operations/get-v2-admin-list-files-list-a7ae1d3d219e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/list/files/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/list/files/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/list/files/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_list_files_list","summary":"Dateimetadaten zu Administratoren abrufen","description":"Liest nicht gelöschte Dateimetadaten mit resource_type=admin. Über filter.resource_id kann ein Administrator ausgewählt werden. Ein Pfadselektor id bezeichnet hier die öffentliche files-ID und wird als Dateifilter verwendet. Kundenspezifische Exportdateien sind über diese generische Ressource ausgeschlossen.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"admin_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Dateimetadaten in generischer v2-Sammlungsdarstellung; kein Dateiinhalt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"files_012345abcdef_123456":{"id":"files_012345abcdef_123456","filename":"unterschrift.png","filetype":"sign","resource_type":"admin","resource_id":"admin_012345abcdef_123456","description":{"de":"Unterschrift"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":true},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":true},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Files.php","method":"getFiles","line":245},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admin/list/workload Auslastung eines Administrators abrufen Liest die zugewiesenen Kurstermine eines Administrators im Zeitraum, einschließlich Angebot, Termin, Ort, Raum und Status. Die JSON-Filter start und end sind erforderlich und werden als Datumswerte verarbeitet. Bei der Listenroute muss der erste Filter id die öffentliche Administrator-ID enthalten. - Operation-ID: get_v2_admin_list_workload - Dokumentation: /docs/api/v2/operations/get-v2-admin-list-workload-7baae5d00fb3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter start/end; bei /list muss der erste Filter id sein. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter start/end; bei /list muss der erste Filter id sein.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"admin_012345abcdef_123456"},{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admin/list/workload' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/list/workload"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/list/workload" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admin_list_workload","summary":"Auslastung eines Administrators abrufen","description":"Liest die zugewiesenen Kurstermine eines Administrators im Zeitraum, einschließlich Angebot, Termin, Ort, Raum und Status. Die JSON-Filter start und end sind erforderlich und werden als Datumswerte verarbeitet. Bei der Listenroute muss der erste Filter id die öffentliche Administrator-ID enthalten.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter start/end; bei /list muss der erste Filter id sein.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"admin_012345abcdef_123456"},{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Spaltendefinitionen mit positionsgleichen Zeilen; limit enthält die Paginierung. Felder/Sortierung/response_type werden für diese Tabelle nicht verwendet.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"offername","type":"string"},{"field":"name","type":"string"},{"field":"offer_id","type":"string"},{"field":"date_id","type":"string"},{"field":"offerdef_id","type":"string"},{"field":"place_id","type":"string"},{"field":"room_id","type":"string"},{"field":"status","type":"integer"},{"field":"date_start","type":"date"},{"field":"date_end","type":"date"}],"rows":[["Beispielkurs","Vormittag","offer_012345abcdef_123456","date_012345abcdef_123456","offerdef_012345abcdef_123456","place_012345abcdef_123456","room_012345abcdef_123456",1,"2026-11-03T09:00:00+01:00","2026-11-03T12:00:00+01:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminfunc.php","line":172},"reason":"Workload SELECT and formatting only; Placefunc194/Roomfunc12 likewise."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"getAdmin","line":568,"branch":{"file":"application/models/v2/Admin.php","line":581}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/admin/remove/files/{id} Administratordatei entfernen Markiert die über ihre öffentliche files-ID ausgewählte Datei als gelöscht. Der übergeordnete Administrator wird dabei nicht gelöscht. Kundenspezifische Exportdateien sind über diese generische Ressource ausgeschlossen. - Operation-ID: delete_v2_admin_remove_files_id - Dokumentation: /docs/api/v2/operations/delete-v2-admin-remove-files-id-7de18088c36f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche files-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]},"description":"Öffentliche files-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/admin/remove/files/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/remove/files/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/remove/files/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_admin_remove_files_id","summary":"Administratordatei entfernen","description":"Markiert die über ihre öffentliche files-ID ausgewählte Datei als gelöscht. Der übergeordnete Administrator wird dabei nicht gelöscht. Kundenspezifische Exportdateien sind über diese generische Ressource ausgeschlossen.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]},"description":"Öffentliche files-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Datei-ID und delete-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Administratordatei entfernen","value":{}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Files.php","line":226,"reason":"Files explicitly rewrites the update/remove third segment to the file ID from segment5; exact Vue resource-file transport."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"deleteAdmin","line":469,"branch":{"file":"application/models/v2/Admin.php","line":493}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admin/task/customized_data Kundenspezifischen Administrator-Export starten Startet den für den Account freigegebenen kundenspezifischen Export. task wählt LuksExport1 (Themenblöcke) oder LuksExport2 (Themenblöcke mit Administratoren); der Taskname wird auf Buchstaben/Ziffern normalisiert. date_start und date_end müssen in Datumswerte umwandelbar sein. Diese Tasks sind nur für ausdrücklich freigeschaltete Accounts vorhanden; nach der Bestätigung wird der Export weiterverarbeitet. - Operation-ID: post_v2_admin_task_customized_data - Dokumentation: /docs/api/v2/operations/post-v2-admin-task-customized-data-91eaf4a76bd8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "task": "LuksExport2", "date_start": "2026-11-01T00:00:00+01:00", "date_end": "2026-12-01T00:00:00+01:00" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admin/task/customized_data' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/task/customized_data"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "task": "LuksExport2", "date_start": "2026-11-01T00:00:00+01:00", "date_end": "2026-12-01T00:00:00+01:00" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/task/customized_data" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"task\":\"LuksExport2\",\"date_start\":\"2026-11-01T00:00:00+01:00\",\"date_end\":\"2026-12-01T00:00:00+01:00\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admin_task_customized_data","summary":"Kundenspezifischen Administrator-Export starten","description":"Startet den für den Account freigegebenen kundenspezifischen Export. task wählt LuksExport1 (Themenblöcke) oder LuksExport2 (Themenblöcke mit Administratoren); der Taskname wird auf Buchstaben/Ziffern normalisiert. date_start und date_end müssen in Datumswerte umwandelbar sein. Diese Tasks sind nur für ausdrücklich freigeschaltete Accounts vorhanden; nach der Bestätigung wird der Export weiterverarbeitet.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bestätigt den Exportstart mit export=pending; enthält noch keine Exportdatei.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":false,"export":"pending"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"const":false},"export":{"const":"pending"}},"additionalProperties":true,"required":["insert","export"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"task":{"type":"string","description":"Implementierte Tasks: LuksExport1 und LuksExport2; nur für freigeschaltete Accounts."},"date_start":{"type":["string","number","boolean","null"]},"date_end":{"type":["string","number","boolean","null"]}},"additionalProperties":true,"required":["task","date_start","date_end"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kundenspezifischen Administrator-Export starten","value":{"data":{"task":"LuksExport2","date_start":"2026-11-01T00:00:00+01:00","date_end":"2026-12-01T00:00:00+01:00"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Admin.php","line":311,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"postAdmin","line":303,"branch":{"file":"application/models/v2/Admin.php","line":311}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/admin/update/files/{id} Metadaten einer Administratordatei ändern Ändert filename und sprachabhängige description einer vorhandenen Datei im Account. Der Pfad enthält die öffentliche files-ID; Dateiinhalt, Eigentümer und filetype werden mit diesem Metadaten-Update nicht ersetzt. Kundenspezifische Exportdateien sind ausgeschlossen. - Operation-ID: put_v2_admin_update_files_id - Dokumentation: /docs/api/v2/operations/put-v2-admin-update-files-id-c2e056f23007/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche files-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]},"description":"Öffentliche files-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "filename": "unterschrift.png", "description": { "de": "Aktuelle Unterschrift" } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/admin/update/files/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/update/files/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "filename": "unterschrift.png", "description": { "de": "Aktuelle Unterschrift" } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admin/update/files/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"filename\":\"unterschrift.png\",\"description\":{\"de\":\"Aktuelle Unterschrift\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_admin_update_files_id","summary":"Metadaten einer Administratordatei ändern","description":"Ändert filename und sprachabhängige description einer vorhandenen Datei im Account. Der Pfad enthält die öffentliche files-ID; Dateiinhalt, Eigentümer und filetype werden mit diesem Metadaten-Update nicht ersetzt. Kundenspezifische Exportdateien sind ausgeschlossen.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]},"description":"Öffentliche files-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Datei-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"filename":{},"description":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Metadaten einer Administratordatei ändern","value":{"data":{"filename":"unterschrift.png","description":{"de":"Aktuelle Unterschrift"}}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Files.php","line":199,"reason":"Files explicitly rewrites the update/remove third segment to the file ID from segment5; exact Vue resource-file transport."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"updateAdmin","line":386,"branch":{"file":"application/models/v2/Admin.php","line":398}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admin/upload/files Datei zu einem Administrator hochladen Lädt eine Datei hoch und verknüpft sie mit dem über resource_id ausgewählten Datensatz. resource_type und filetype müssen im Metadatenobjekt gesetzt sein; für Administratoren üblicherweise admin mit sign oder avatar. Es gelten die Owner-Prüfung, MIME-/Größenprüfung sowie bei Admin-Avataren gegebenenfalls die Gesichtserkennung. Signaturen und Avatare ersetzen vorhandene Dateien desselben Typs durch Soft-Löschung. - Operation-ID: post_v2_admin_upload_files - Dokumentation: /docs/api/v2/operations/post-v2-admin-upload-files-88005c68370d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admin/upload/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admin/upload/files"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/admin/upload/files" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admin_upload_files","summary":"Datei zu einem Administrator hochladen","description":"Lädt eine Datei hoch und verknüpft sie mit dem über resource_id ausgewählten Datensatz. resource_type und filetype müssen im Metadatenobjekt gesetzt sein; für Administratoren üblicherweise admin mit sign oder avatar. Es gelten die Owner-Prüfung, MIME-/Größenprüfung sowie bei Admin-Avataren gegebenenfalls die Gesichtserkennung. Signaturen und Avatare ersetzen vorhandene Dateien desselben Typs durch Soft-Löschung.","tags":["admin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche ID der neu gespeicherten Datei und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Dateibytes. Avatare: JPEG/PNG/WebP, höchstens 8 MiB; andere Bilder höchstens 2 MiB, Angebotsanhänge höchstens 20 MiB."},"meta":{"type":"string","description":"JSON-Zeichenfolge mit dem Envelope {data:{resource_id,resource_type,filetype,...}}; alternativ direkte Multipart-Formfelder.","examples":["{\"data\":{\"resource_id\":\"admin_012345abcdef_123456\",\"filename\":\"unterschrift.png\",\"resource_type\":\"admin\",\"filetype\":\"sign\"}}"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$"},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$"},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$"}],"description":"Öffentliche Eigentümer-ID bei direkten Multipart-Feldern."},"filename":{"type":"string"},"resource_type":{"enum":["admin","user","offer"]},"filetype":{"enum":["avatar","sign","image2","image3","internalOfferFile","externalOfferFile"]}},"required":["file"],"anyOf":[{"required":["meta"]},{"required":["resource_id","resource_type","filetype"]}],"additionalProperties":true},"examples":{"upload":{"summary":"Synthetische Metadaten; file durch echte Dateibytes ersetzen.","value":{"file":"","meta":"{\"data\":{\"resource_id\":\"admin_012345abcdef_123456\",\"filename\":\"unterschrift.png\",\"resource_type\":\"admin\",\"filetype\":\"sign\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admin.php","method":"postAdmin","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admin","short":"admi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/adminconfig Persönliche Administratorkonfiguration löschen Löscht die gespeicherte persönliche Administratorkonfiguration vollständig. Die globale config-LUT-Vorlage und das Administratorprofil bleiben erhalten. - Operation-ID: delete_v2_adminconfig - Dokumentation: /docs/api/v2/operations/delete-v2-adminconfig-81d7d14d9292/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "adminconfig_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/adminconfig' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "adminconfig_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"adminconfig_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_adminconfig","summary":"Persönliche Administratorkonfiguration löschen","description":"Löscht die gespeicherte persönliche Administratorkonfiguration vollständig. Die globale config-LUT-Vorlage und das Administratorprofil bleiben erhalten.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche adminconfig-ID und delete-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"adminconfig_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Persönliche Administratorkonfiguration löschen","value":{"id":"adminconfig_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"deleteAdminconfig","line":138},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/adminconfig Persönliche Administratorkonfigurationen abrufen Liest gespeicherte Konfigurationen mit zugehöriger Administrator- und LUT-Konfigurations-ID. Das Feld data enthält decodiertes JSON; seine Schlüssel sind durch die gewählte Konfiguration bestimmt und absichtlich kein festes globales Schema. filter.admin und filter.config begrenzen die Auswahl. - Operation-ID: get_v2_adminconfig - Dokumentation: /docs/api/v2/operations/get-v2-adminconfig-73e949bd1087/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/adminconfig' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_adminconfig","summary":"Persönliche Administratorkonfigurationen abrufen","description":"Liest gespeicherte Konfigurationen mit zugehöriger Administrator- und LUT-Konfigurations-ID. Das Feld data enthält decodiertes JSON; seine Schlüssel sind durch die gewählte Konfiguration bestimmt und absichtlich kein festes globales Schema. filter.admin und filter.config begrenzen die Auswahl.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generische Sammlung gespeicherter Konfigurationen mit decodiertem data.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"adminconfig_012345abcdef_123456":{"id":"adminconfig_012345abcdef_123456","config":"config_012345abcdef_123456","admin":"admin_012345abcdef_123456","data":{"columns":["first_name","last_name"],"page_size":25},"last_change":"2026-10-22T10:00:00+02:00"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"config":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"data":{"description":"Decodierter JSON-Wert; Konfigurationsschlüssel und Werte hängen von der gewählten LUT-Konfiguration ab."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"config":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"data":{"description":"Decodierter JSON-Wert; Konfigurationsschlüssel und Werte hängen von der gewählten LUT-Konfiguration ab."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"getAdminconfig","line":154},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/adminconfig Persönliche Administratorkonfiguration anlegen Speichert data für die Kombination aus admin und config. Beide öffentlichen IDs und data sind erforderlich. Eine bereits vorhandene Kombination wird abgelehnt. data akzeptiert ein JSON-Objekt, eine Liste oder eine JSON-Zeichenfolge; der Server decodiert und speichert den Wert. Die Struktur ist konfigurationsabhängig. Ein Stapel von Datensätzen wird vom generischen Einfügepfad unterstützt. - Operation-ID: post_v2_adminconfig - Dokumentation: /docs/api/v2/operations/post-v2-adminconfig-c79b7c9614b5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "config": "config_012345abcdef_123456", "admin": "admin_012345abcdef_123456", "data": { "columns": [ "first_name", "last_name" ], "page_size": 25 } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/adminconfig' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "config": "config_012345abcdef_123456", "admin": "admin_012345abcdef_123456", "data": { "columns": [ "first_name", "last_name" ], "page_size": 25 } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"config\":\"config_012345abcdef_123456\",\"admin\":\"admin_012345abcdef_123456\",\"data\":{\"columns\":[\"first_name\",\"last_name\"],\"page_size\":25}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_adminconfig","summary":"Persönliche Administratorkonfiguration anlegen","description":"Speichert data für die Kombination aus admin und config. Beide öffentlichen IDs und data sind erforderlich. Eine bereits vorhandene Kombination wird abgelehnt. data akzeptiert ein JSON-Objekt, eine Liste oder eine JSON-Zeichenfolge; der Server decodiert und speichert den Wert. Die Struktur ist konfigurationsabhängig. Ein Stapel von Datensätzen wird vom generischen Einfügepfad unterstützt.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche adminconfig-ID beziehungsweise Liste von IDs und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"adminconfig_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"config":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["config_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38},"admin":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":52},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"type":"array","items":{}},{"type":"string"}],"description":"Decoded object/array or a JSON-encoded string. The callback decodes string input before JSON/base64 storage; action-specific semantics remain dynamic.","x-edoobox-contractSource":[{"file":"application/models/v2/Adminconfigfunc.php","line":7,"reason":"PUT accepts decoded arrays or decodes JSON string input.","sha256":"62186f93d05e34bcdfb65ca754cb101f75b25f7788c0cc647c6f27dfa0bad43b"},{"file":"application/models/v2/Adminconfigfunc.php","line":58,"reason":"POST before-insert transforms the same dynamic config payload.","sha256":"62186f93d05e34bcdfb65ca754cb101f75b25f7788c0cc647c6f27dfa0bad43b"},{"file":"application/models/v2/Adminconfig.php","line":71,"reason":"Response json_decode can return dynamic JSON values.","sha256":"7d2df83372bb7f880dad48503c6ab222b529c41cb136d3fbbb30979fe02c0deb"}]},"last_change":{"readOnly":true,"description":"Getter ignores input and returns time(). insert_default injects it on omission when present; omission otherwise does not call the getter. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Adminconfig.php","line":80,"lineStart":80,"lineEnd":91,"sha256":"7d2df83372bb7f880dad48503c6ab222b529c41cb136d3fbbb30979fe02c0deb","spanSha256":"96608b40202214fc8c808f25f65a2f7a426b711c2d0e9c79c7d8698ab9b9bb5d","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Adminconfig.php","line":104,"lineStart":104,"lineEnd":128,"sha256":"7d2df83372bb7f880dad48503c6ab222b529c41cb136d3fbbb30979fe02c0deb","spanSha256":"dd5ca52305aef34e38c7a8e625b743be46f460fd72f1e6a5bcb136f948cd8628","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["config","admin","data"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"config":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["config_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38},"admin":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":52},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"type":"array","items":{}},{"type":"string"}],"description":"Decoded object/array or a JSON-encoded string. The callback decodes string input before JSON/base64 storage; action-specific semantics remain dynamic.","x-edoobox-contractSource":[{"file":"application/models/v2/Adminconfigfunc.php","line":7,"reason":"PUT accepts decoded arrays or decodes JSON string input.","sha256":"62186f93d05e34bcdfb65ca754cb101f75b25f7788c0cc647c6f27dfa0bad43b"},{"file":"application/models/v2/Adminconfigfunc.php","line":58,"reason":"POST before-insert transforms the same dynamic config payload.","sha256":"62186f93d05e34bcdfb65ca754cb101f75b25f7788c0cc647c6f27dfa0bad43b"},{"file":"application/models/v2/Adminconfig.php","line":71,"reason":"Response json_decode can return dynamic JSON values.","sha256":"7d2df83372bb7f880dad48503c6ab222b529c41cb136d3fbbb30979fe02c0deb"}]},"last_change":{"readOnly":true,"description":"Getter ignores input and returns time(). insert_default injects it on omission when present; omission otherwise does not call the getter. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Adminconfig.php","line":80,"lineStart":80,"lineEnd":91,"sha256":"7d2df83372bb7f880dad48503c6ab222b529c41cb136d3fbbb30979fe02c0deb","spanSha256":"96608b40202214fc8c808f25f65a2f7a426b711c2d0e9c79c7d8698ab9b9bb5d","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Adminconfig.php","line":104,"lineStart":104,"lineEnd":128,"sha256":"7d2df83372bb7f880dad48503c6ab222b529c41cb136d3fbbb30979fe02c0deb","spanSha256":"dd5ca52305aef34e38c7a8e625b743be46f460fd72f1e6a5bcb136f948cd8628","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["config","admin","data"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Persönliche Administratorkonfiguration anlegen","value":{"data":{"config":"config_012345abcdef_123456","admin":"admin_012345abcdef_123456","data":{"columns":["first_name","last_name"],"page_size":25}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"postAdminconfig","line":104},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/adminconfig Persönliche Administratorkonfiguration ändern Ersetzt den JSON-Inhalt data einer gespeicherten Konfiguration; admin und config sind unveränderlich. Akzeptiert decodierte Objekte/Listen oder JSON-Zeichenfolgen und aktualisiert last_change serverseitig. Die konkreten Konfigurationsschlüssel bleiben durch die Anwendung der LUT-Vorlage bestimmt. - Operation-ID: put_v2_adminconfig - Dokumentation: /docs/api/v2/operations/put-v2-adminconfig-69f3b463d5d9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "data": { "columns": [ "first_name", "last_name" ], "page_size": 50 } }, "id": "adminconfig_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/adminconfig' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "data": { "columns": [ "first_name", "last_name" ], "page_size": 50 } }, "id": "adminconfig_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"data\":{\"columns\":[\"first_name\",\"last_name\"],\"page_size\":50}},\"id\":\"adminconfig_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_adminconfig","summary":"Persönliche Administratorkonfiguration ändern","description":"Ersetzt den JSON-Inhalt data einer gespeicherten Konfiguration; admin und config sind unveränderlich. Akzeptiert decodierte Objekte/Listen oder JSON-Zeichenfolgen und aktualisiert last_change serverseitig. Die konkreten Konfigurationsschlüssel bleiben durch die Anwendung der LUT-Vorlage bestimmt.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche adminconfig-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"adminconfig_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"config":false,"admin":false,"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"type":"array","items":{}},{"type":"string"}],"description":"Decoded object/array or a JSON-encoded string. The callback decodes string input before JSON/base64 storage; action-specific semantics remain dynamic.","x-edoobox-contractSource":[{"file":"application/models/v2/Adminconfigfunc.php","line":7,"reason":"PUT accepts decoded arrays or decodes JSON string input.","sha256":"62186f93d05e34bcdfb65ca754cb101f75b25f7788c0cc647c6f27dfa0bad43b"},{"file":"application/models/v2/Adminconfigfunc.php","line":58,"reason":"POST before-insert transforms the same dynamic config payload.","sha256":"62186f93d05e34bcdfb65ca754cb101f75b25f7788c0cc647c6f27dfa0bad43b"},{"file":"application/models/v2/Adminconfig.php","line":71,"reason":"Response json_decode can return dynamic JSON values.","sha256":"7d2df83372bb7f880dad48503c6ab222b529c41cb136d3fbbb30979fe02c0deb"}]},"last_change":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["config","admin","last_change"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Persönliche Administratorkonfiguration ändern","value":{"data":{"data":{"columns":["first_name","last_name"],"page_size":50}},"id":"adminconfig_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"updateAdminconfig","line":121},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/adminconfig/{id} Persönliche Administratorkonfiguration löschen Löscht die gespeicherte persönliche Administratorkonfiguration vollständig. Die globale config-LUT-Vorlage und das Administratorprofil bleiben erhalten. - Operation-ID: delete_v2_adminconfig_id - Dokumentation: /docs/api/v2/operations/delete-v2-adminconfig-id-2aa90481f8d5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/adminconfig/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_adminconfig_id","summary":"Persönliche Administratorkonfiguration löschen","description":"Löscht die gespeicherte persönliche Administratorkonfiguration vollständig. Die globale config-LUT-Vorlage und das Administratorprofil bleiben erhalten.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche adminconfig-ID und delete-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"adminconfig_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Persönliche Administratorkonfiguration löschen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"deleteAdminconfig","line":138},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/adminconfig/{id} Persönliche Administratorkonfigurationen abrufen Liest gespeicherte Konfigurationen mit zugehöriger Administrator- und LUT-Konfigurations-ID. Das Feld data enthält decodiertes JSON; seine Schlüssel sind durch die gewählte Konfiguration bestimmt und absichtlich kein festes globales Schema. filter.admin und filter.config begrenzen die Auswahl. - Operation-ID: get_v2_adminconfig_id - Dokumentation: /docs/api/v2/operations/get-v2-adminconfig-id-6df63f162547/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/adminconfig/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_adminconfig_id","summary":"Persönliche Administratorkonfigurationen abrufen","description":"Liest gespeicherte Konfigurationen mit zugehöriger Administrator- und LUT-Konfigurations-ID. Das Feld data enthält decodiertes JSON; seine Schlüssel sind durch die gewählte Konfiguration bestimmt und absichtlich kein festes globales Schema. filter.admin und filter.config begrenzen die Auswahl.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generische Sammlung gespeicherter Konfigurationen mit decodiertem data.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"adminconfig_012345abcdef_123456":{"id":"adminconfig_012345abcdef_123456","config":"config_012345abcdef_123456","admin":"admin_012345abcdef_123456","data":{"columns":["first_name","last_name"],"page_size":25},"last_change":"2026-10-22T10:00:00+02:00"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"config":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"data":{"description":"Decodierter JSON-Wert; Konfigurationsschlüssel und Werte hängen von der gewählten LUT-Konfiguration ab."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"config":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"data":{"description":"Decodierter JSON-Wert; Konfigurationsschlüssel und Werte hängen von der gewählten LUT-Konfiguration ab."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"getAdminconfig","line":154},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/adminconfig/{id} Persönliche Administratorkonfiguration ändern Ersetzt den JSON-Inhalt data einer gespeicherten Konfiguration; admin und config sind unveränderlich. Akzeptiert decodierte Objekte/Listen oder JSON-Zeichenfolgen und aktualisiert last_change serverseitig. Die konkreten Konfigurationsschlüssel bleiben durch die Anwendung der LUT-Vorlage bestimmt. - Operation-ID: put_v2_adminconfig_id - Dokumentation: /docs/api/v2/operations/put-v2-adminconfig-id-6f8770904dea/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "data": { "columns": [ "first_name", "last_name" ], "page_size": 50 } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/adminconfig/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "data": { "columns": [ "first_name", "last_name" ], "page_size": 50 } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"data\":{\"columns\":[\"first_name\",\"last_name\"],\"page_size\":50}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_adminconfig_id","summary":"Persönliche Administratorkonfiguration ändern","description":"Ersetzt den JSON-Inhalt data einer gespeicherten Konfiguration; admin und config sind unveränderlich. Akzeptiert decodierte Objekte/Listen oder JSON-Zeichenfolgen und aktualisiert last_change serverseitig. Die konkreten Konfigurationsschlüssel bleiben durch die Anwendung der LUT-Vorlage bestimmt.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche adminconfig-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"adminconfig_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"config":false,"admin":false,"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"type":"array","items":{}},{"type":"string"}],"description":"Decoded object/array or a JSON-encoded string. The callback decodes string input before JSON/base64 storage; action-specific semantics remain dynamic.","x-edoobox-contractSource":[{"file":"application/models/v2/Adminconfigfunc.php","line":7,"reason":"PUT accepts decoded arrays or decodes JSON string input.","sha256":"62186f93d05e34bcdfb65ca754cb101f75b25f7788c0cc647c6f27dfa0bad43b"},{"file":"application/models/v2/Adminconfigfunc.php","line":58,"reason":"POST before-insert transforms the same dynamic config payload.","sha256":"62186f93d05e34bcdfb65ca754cb101f75b25f7788c0cc647c6f27dfa0bad43b"},{"file":"application/models/v2/Adminconfig.php","line":71,"reason":"Response json_decode can return dynamic JSON values.","sha256":"7d2df83372bb7f880dad48503c6ab222b529c41cb136d3fbbb30979fe02c0deb"}]},"last_change":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["config","admin","last_change"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["adminconfig_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Persönliche Administratorkonfiguration ändern","value":{"data":{"data":{"columns":["first_name","last_name"],"page_size":50}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"updateAdminconfig","line":121},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/adminconfig/{id}/LUT Konfigurationsvorlagen für Administratoren abrufen Liest die globalen LUT-Vorlagen mit Name und decodiertem JSON. Diese Vorlagen dienen als config-Auswahl beim Anlegen einer persönlichen Administratorkonfiguration; ihre IDs tragen den Typ config. Die Vorlage ist keine persönliche adminconfig. - Operation-ID: get_v2_adminconfig_id_LUT - Dokumentation: /docs/api/v2/operations/get-v2-adminconfig-id-lut-871cf36a8b2a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche ID der LUT-Konfiguration, Typ config. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["config_012345abcdef_123456"]},"description":"Öffentliche ID der LUT-Konfiguration, Typ config."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/adminconfig/'"${PATH_ID}"'/LUT' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig/{id}/LUT"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig/{id}/LUT" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_adminconfig_id_LUT","summary":"Konfigurationsvorlagen für Administratoren abrufen","description":"Liest die globalen LUT-Vorlagen mit Name und decodiertem JSON. Diese Vorlagen dienen als config-Auswahl beim Anlegen einer persönlichen Administratorkonfiguration; ihre IDs tragen den Typ config. Die Vorlage ist keine persönliche adminconfig.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["config_012345abcdef_123456"]},"description":"Öffentliche ID der LUT-Konfiguration, Typ config."}],"responses":{"200":{"description":"Sammlung von config-Vorlagen in der generischen v2-Darstellung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"config_012345abcdef_123456":{"id":"config_012345abcdef_123456","name":"Beispieltabelle","data":{"columns":["first_name","last_name"]},"last_change":"2026-10-22T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"},"data":{"description":"Decodiertes, konfigurationsabhängiges JSON der Vorlage."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"},"data":{"description":"Decodiertes, konfigurationsabhängiges JSON der Vorlage."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Adminconfig.php","line":158,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminconfig.php","line":154},"reason":"Local Structure selection and GET_data, or literal task lookup values; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"getAdminconfig","line":154,"branch":{"file":"application/models/v2/Adminconfig.php","line":158}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/adminconfig/list Persönliche Administratorkonfigurationen abrufen Liest gespeicherte Konfigurationen mit zugehöriger Administrator- und LUT-Konfigurations-ID. Das Feld data enthält decodiertes JSON; seine Schlüssel sind durch die gewählte Konfiguration bestimmt und absichtlich kein festes globales Schema. filter.admin und filter.config begrenzen die Auswahl. - Operation-ID: get_v2_adminconfig_list - Dokumentation: /docs/api/v2/operations/get-v2-adminconfig-list-54bf79c1068b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/adminconfig/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_adminconfig_list","summary":"Persönliche Administratorkonfigurationen abrufen","description":"Liest gespeicherte Konfigurationen mit zugehöriger Administrator- und LUT-Konfigurations-ID. Das Feld data enthält decodiertes JSON; seine Schlüssel sind durch die gewählte Konfiguration bestimmt und absichtlich kein festes globales Schema. filter.admin und filter.config begrenzen die Auswahl.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generische Sammlung gespeicherter Konfigurationen mit decodiertem data.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"adminconfig_012345abcdef_123456":{"id":"adminconfig_012345abcdef_123456","config":"config_012345abcdef_123456","admin":"admin_012345abcdef_123456","data":{"columns":["first_name","last_name"],"page_size":25},"last_change":"2026-10-22T10:00:00+02:00"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"config":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"data":{"description":"Decodierter JSON-Wert; Konfigurationsschlüssel und Werte hängen von der gewählten LUT-Konfiguration ab."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^adminconfig_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"config":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"data":{"description":"Decodierter JSON-Wert; Konfigurationsschlüssel und Werte hängen von der gewählten LUT-Konfiguration ab."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"getAdminconfig","line":154},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/adminconfig/list/LUT Konfigurationsvorlagen für Administratoren abrufen Liest die globalen LUT-Vorlagen mit Name und decodiertem JSON. Diese Vorlagen dienen als config-Auswahl beim Anlegen einer persönlichen Administratorkonfiguration; ihre IDs tragen den Typ config. Die Vorlage ist keine persönliche adminconfig. - Operation-ID: get_v2_adminconfig_list_LUT - Dokumentation: /docs/api/v2/operations/get-v2-adminconfig-list-lut-59c385abdd19/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/adminconfig/list/LUT' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/adminconfig/list/LUT"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/adminconfig/list/LUT" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_adminconfig_list_LUT","summary":"Konfigurationsvorlagen für Administratoren abrufen","description":"Liest die globalen LUT-Vorlagen mit Name und decodiertem JSON. Diese Vorlagen dienen als config-Auswahl beim Anlegen einer persönlichen Administratorkonfiguration; ihre IDs tragen den Typ config. Die Vorlage ist keine persönliche adminconfig.","tags":["adminconfig"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"adminconfig_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Sammlung von config-Vorlagen in der generischen v2-Darstellung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"config_012345abcdef_123456":{"id":"config_012345abcdef_123456","name":"Beispieltabelle","data":{"columns":["first_name","last_name"]},"last_change":"2026-10-22T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"},"data":{"description":"Decodiertes, konfigurationsabhängiges JSON der Vorlage."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^config_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"},"data":{"description":"Decodiertes, konfigurationsabhängiges JSON der Vorlage."},"last_change":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminconfig.php","line":154},"reason":"Local Structure selection and GET_data, or literal task lookup values; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Adminconfig.php","method":"getAdminconfig","line":154,"branch":{"file":"application/models/v2/Adminconfig.php","line":158}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"adminconfig","short":"adcg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admission/admin Angemeldeten Leiter und Präsenzkonfiguration laden Liest Profil und ACL des angemeldeten Administrators sowie Kontoname, Sprache, Zeitzone und attendance_config. Nur ein Administratorkontext ist zulässig; display_all_dates kennzeichnet erweiterte Terminrechte. - Operation-ID: get_v2_admission_admin - Dokumentation: /docs/api/v2/operations/get-v2-admission-admin-512b30ac2a01/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admission/admin' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/admin"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/admin" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admission_admin","summary":"Angemeldeten Leiter und Präsenzkonfiguration laden","description":"Liest Profil und ACL des angemeldeten Administrators sowie Kontoname, Sprache, Zeitzone und attendance_config. Nur ein Administratorkontext ist zulässig; display_all_dates kennzeichnet erweiterte Terminrechte.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"admin_012345abcdef_123456","first_name":"Alex","last_name":"Beispiel","email":"alex@example.invalid","language":"de","acl":2,"shortcut":"demo","organization_name":"Beispiel Akademie","timezone":"Europe/Zurich","display_all_dates":true,"attendance_config":[],"account_language":"de"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string"},"language":{"type":"string"},"acl":{"type":"integer"},"shortcut":{"type":"string"},"organization_name":{"type":"string"},"timezone":{"type":"string"},"display_all_dates":{"type":"boolean"},"attendance_config":{"type":"array","items":{"type":"object","properties":{"status":{"x-edoobox-canonicalType":"integer","description":"Gespeicherter Konfigurationswert; wird hier nicht neu typisiert."},"order":{"type":"integer"}},"additionalProperties":true}},"account_language":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"getAdmission","line":136,"branch":{"file":"application/models/v2/Admission.php","line":145}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/admission/admissioncontrol/{code} Einlassprüfung im Leiterkontext durchführen Prüft zuerst den Leiter-/Adminzugang auf jeden Termin aller validity-Gruppen und verarbeitet danach den Barcode aus code. participant löst Mehrdeutigkeiten auf. Auch bei error=false fachliche data.error und validity[*].error prüfen. - Operation-ID: put_v2_admission_admissioncontrol_code - Dokumentation: /docs/api/v2/operations/put-v2-admission-admissioncontrol-code-0b4443beb227/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | code | path | ja | | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"in":"path","name":"code","required":true,"schema":{"type":"string"}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CODE:?PATH_CODE setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "validity": [ { "dates": [ "date_012345abcdef_123456" ], "behavior": {}, "attendance": { "set": { "type": 1 }, "alreadyset": { "check": true, "set": { "type": 2 } } } } ], "participant": "participant_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/admission/admissioncontrol/'"${PATH_CODE}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/admissioncontrol/{code}"; path = path.replaceAll("{code}", encodeURIComponent(required("PATH_CODE"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "validity": [ { "dates": [ "date_012345abcdef_123456" ], "behavior": {}, "attendance": { "set": { "type": 1 }, "alreadyset": { "check": true, "set": { "type": 2 } } } } ], "participant": "participant_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/admissioncontrol/{code}" path = path.replace("{code}", urllib.parse.quote(os.environ["PATH_CODE"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"validity\":[{\"dates\":[\"date_012345abcdef_123456\"],\"behavior\":{},\"attendance\":{\"set\":{\"type\":1},\"alreadyset\":{\"check\":true,\"set\":{\"type\":2}}}}],\"participant\":\"participant_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_admission_admissioncontrol_code","summary":"Einlassprüfung im Leiterkontext durchführen","description":"Prüft zuerst den Leiter-/Adminzugang auf jeden Termin aller validity-Gruppen und verarbeitet danach den Barcode aus code. participant löst Mehrdeutigkeiten auf. Auch bei error=false fachliche data.error und validity[*].error prüfen.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"in":"path","name":"code","required":true,"schema":{"type":"string"}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Einlass-/Präsenzresultate wie bei admissioncontrol, ergänzt um erreichbare Kunden-/Preisnamen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"code":"DEMOX377","error":[],"barcode":{"user":"user_012345abcdef_123456","status":true},"validity":[{"data":[],"error":[]}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"code":{"type":["string","boolean"]},"error":{"type":"array","items":{"type":"string"}},"barcode":{"type":"object","properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"status":{"type":"boolean"},"user_name":{"type":"string"},"pricecategory_name":{"type":"string"}},"additionalProperties":true},"validity":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"data":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"lesson_booking":{"type":"boolean"},"date":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"waiting_list":{"type":"boolean"},"time":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}},"additionalProperties":true},{"const":false}]},"nobooking":{"type":"boolean"},"nobookingdate":{"type":"boolean"},"alreadyset":{"type":"boolean"},"attendance":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"type":{"type":"integer"}},"additionalProperties":true},{"const":false}]},"set":{"type":["integer","boolean"]}},"additionalProperties":true}}},"additionalProperties":true},"items":{}},"error":{"type":"array","items":{"type":"string"}},"require":{"type":"object","properties":{},"additionalProperties":true},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant_candidates":{"type":"array","items":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"data":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"lesson_booking":{"type":"boolean"},"date":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"waiting_list":{"type":"boolean"},"time":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}},"additionalProperties":true},{"const":false}]},"nobooking":{"type":"boolean"},"nobookingdate":{"type":"boolean"},"alreadyset":{"type":"boolean"},"attendance":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"type":{"type":"integer"}},"additionalProperties":true},{"const":false}]},"set":{"type":["integer","boolean"]}},"additionalProperties":true}}},"additionalProperties":true},"items":{}},"error":{"type":"array","items":{"type":"string"}},"require":{"type":"object","properties":{},"additionalProperties":true},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant_candidates":{"type":"array","items":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}}},"additionalProperties":true}}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"validity":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"dates":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minItems":1,"maxItems":20},{"type":"object","additionalProperties":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minProperties":1,"maxProperties":20}]},"behavior":{"type":["object","array"],"properties":{"existing":{},"nobooking":{},"nobookingdate":{}},"additionalProperties":true,"items":{},"description":"Der Container ist Pflicht. Die genannten Optionen werden eingelesen; die aktuelle Implementierung meldet bestehende/fehlende Buchungen unabhängig von diesen Werten."},"attendance":{"type":"object","properties":{"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]},"alreadyset":{"type":"object","properties":{"check":{"x-edoobox-canonicalType":"boolean","description":"Mit bool normalisiert; ohne check wird true verwendet."},"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]}},"additionalProperties":true,"required":["set"]}},"additionalProperties":true,"required":["set","alreadyset"]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]}},"additionalProperties":true,"required":["dates","behavior","attendance"]},"minItems":1,"maxItems":5},{"type":"object","additionalProperties":{"type":"object","properties":{"dates":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minItems":1,"maxItems":20},{"type":"object","additionalProperties":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minProperties":1,"maxProperties":20}]},"behavior":{"type":["object","array"],"properties":{"existing":{},"nobooking":{},"nobookingdate":{}},"additionalProperties":true,"items":{},"description":"Der Container ist Pflicht. Die genannten Optionen werden eingelesen; die aktuelle Implementierung meldet bestehende/fehlende Buchungen unabhängig von diesen Werten."},"attendance":{"type":"object","properties":{"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]},"alreadyset":{"type":"object","properties":{"check":{"x-edoobox-canonicalType":"boolean","description":"Mit bool normalisiert; ohne check wird true verwendet."},"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]}},"additionalProperties":true,"required":["set"]}},"additionalProperties":true,"required":["set","alreadyset"]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]}},"additionalProperties":true,"required":["dates","behavior","attendance"]},"minProperties":1,"maxProperties":5}]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]},"ignore_expired_code":{"x-edoobox-canonicalType":"boolean","description":"Ein truthy-Wert erlaubt die weitere Prüfung trotz abgelaufenem Barcode; data.error enthält weiterhin code_expired."}},"additionalProperties":true,"required":["validity"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Einlassprüfung im Leiterkontext durchführen","value":{"data":{"validity":[{"dates":["date_012345abcdef_123456"],"behavior":{},"attendance":{"set":{"type":1},"alreadyset":{"check":true,"set":{"type":2}}}}],"participant":"participant_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"updateAdmission","line":63,"branch":{"file":"application/models/v2/Admission.php","line":76}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admission/attendance Präsenz im Leiterkontext erfassen Erstellt Präsenz für data.date, data.participant und data.type. Der Termin muss dem angemeldeten Leiter beziehungsweise dem berechtigten Administratorkonto zugänglich sein. Die allgemeinen Regeln gelten: nur ein Datensatz je Teilnehmer/Termin und beide im selben Angebot. - Operation-ID: post_v2_admission_attendance - Dokumentation: /docs/api/v2/operations/post-v2-admission-attendance-09de15417f07/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "date": "date_012345abcdef_123456", "participant": "participant_012345abcdef_123456", "type": 1 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admission/attendance' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/attendance"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "date": "date_012345abcdef_123456", "participant": "participant_012345abcdef_123456", "type": 1 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/attendance" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"date\":\"date_012345abcdef_123456\",\"participant\":\"participant_012345abcdef_123456\",\"type\":1}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admission_attendance","summary":"Präsenz im Leiterkontext erfassen","description":"Erstellt Präsenz für data.date, data.participant und data.type. Der Termin muss dem angemeldeten Leiter beziehungsweise dem berechtigten Administratorkonto zugänglich sein. Die allgemeinen Regeln gelten: nur ein Datensatz je Teilnehmer/Termin und beide im selben Angebot.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"attendance_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},"type":{"x-edoobox-canonicalType":"integer","description":"Mit int normalisierter Präsenzstatus; gültige Statuswerte hängen von attendance_config ab."}},"additionalProperties":true,"required":["date","participant","type"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Präsenz im Leiterkontext erfassen","value":{"data":{"date":"date_012345abcdef_123456","participant":"participant_012345abcdef_123456","type":1}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"postAdmission","line":19,"branch":{"file":"application/models/v2/Admission.php","line":35}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admission/attendance_view Teilnehmer und Präsenz eines Leitertermins laden Lädt Angebot, benannte Teilnehmer, Präsenz und Einladungslinks unbenannter Plätze für den Termin aus filter.date. Der Termin muss dem Leiter gehören; ACL 2/3 erlaubt Termine des Kontos ohne Leiterzuordnung. Bei Lektionenangeboten werden nur gebuchte Teilnehmer samt date_participants geladen. - Operation-ID: get_v2_admission_attendance_view - Dokumentation: /docs/api/v2/operations/get-v2-admission-attendance-view-1557971a3d19/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste mit den angegebenen Selektoren. | | limit | query | nein | Der Dispatcher normalisiert start/reply und die Antwort gibt diese Metadaten zurück; die aktuelle Teilnehmer-SQL-Abfrage wird damit nicht paginiert. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit den angegebenen Selektoren.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"date"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"date","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"Der Dispatcher normalisiert start/reply und die Antwort gibt diese Metadaten zurück; die aktuelle Teilnehmer-SQL-Abfrage wird damit nicht paginiert.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer"},"reply":{"type":"integer"}},"additionalProperties":true},"example":{"start":0,"reply":100}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admission/attendance_view' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/attendance_view"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/attendance_view" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admission_attendance_view","summary":"Teilnehmer und Präsenz eines Leitertermins laden","description":"Lädt Angebot, benannte Teilnehmer, Präsenz und Einladungslinks unbenannter Plätze für den Termin aus filter.date. Der Termin muss dem Leiter gehören; ACL 2/3 erlaubt Termine des Kontos ohne Leiterzuordnung. Bei Lektionenangeboten werden nur gebuchte Teilnehmer samt date_participants geladen.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit den angegebenen Selektoren.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"date"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"date","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"Der Dispatcher normalisiert start/reply und die Antwort gibt diese Metadaten zurück; die aktuelle Teilnehmer-SQL-Abfrage wird damit nicht paginiert.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer"},"reply":{"type":"integer"}},"additionalProperties":true},"example":{"start":0,"reply":100}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält Teilnehmer-/Präsenzlisten und Angebot; limit steht neben data. Unbenannte Plätze enthalten öffentliche Teilnehmer-IDs und zeitlich begrenzte Einladungslinks.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offer":{"id":"offer_012345abcdef_123456","name":"Einführungskurs","number":"K-2026-01","user_maximum":20,"lesson_booking":false,"date_start":"2026-11-02T09:00:00+01:00","date_end":"2026-11-02T12:00:00+01:00"},"participants":[],"attendances":[],"unnamed_participants":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"offer":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"name":{"type":"string"},"number":{"type":"string"},"user_maximum":{"type":"integer"},"lesson_booking":{"type":"boolean"},"date_start":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"date_end":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]}},"additionalProperties":true},"participants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"company":{"type":"string"},"phone_number":{"type":"string"},"email":{"type":"string"},"pricecategory_name":{"type":"string"}},"additionalProperties":true}},"attendances":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"last_change":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"type":{"type":"integer"}},"additionalProperties":true}},"unnamed_participants":{"type":"array","items":{"type":"object","properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"company":{"type":"string"},"phone_number":{"type":"string"},"email":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"unnamed_participants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},"url_signup":{"type":"string","format":"uri"},"pricecategory_name":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true}},"date_participants":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"getAdmission","line":136,"branch":{"file":"application/models/v2/Admission.php","line":152}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/admission/attendance/{attendanceId} Präsenz im Leiterkontext ändern Ändert type des vorhandenen Präsenzdatensatzes aus attendanceId. Der Handler prüft Terminzugang im Leiter-/Admin-Kontext, aktualisiert Präsenz und protokolliert den Statuswechsel. - Operation-ID: put_v2_admission_attendance_attendanceId - Dokumentation: /docs/api/v2/operations/put-v2-admission-attendance-attendanceid-fc6eecd3f4f7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | attendanceId | path | ja | | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"in":"path","name":"attendanceId","required":true,"schema":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ATTENDANCEID:?PATH_ATTENDANCEID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "type": 1 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/admission/attendance/'"${PATH_ATTENDANCEID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/attendance/{attendanceId}"; path = path.replaceAll("{attendanceId}", encodeURIComponent(required("PATH_ATTENDANCEID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "type": 1 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/attendance/{attendanceId}" path = path.replace("{attendanceId}", urllib.parse.quote(os.environ["PATH_ATTENDANCEID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"type\":1}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_admission_attendance_attendanceId","summary":"Präsenz im Leiterkontext ändern","description":"Ändert type des vorhandenen Präsenzdatensatzes aus attendanceId. Der Handler prüft Terminzugang im Leiter-/Admin-Kontext, aktualisiert Präsenz und protokolliert den Statuswechsel.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"in":"path","name":"attendanceId","required":true,"schema":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche attendance-ID und update=true nach erfolgreicher Aktualisierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"attendance_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit int normalisierter Präsenzstatus; Statuskatalog des Kontos beachten."}},"additionalProperties":true,"required":["type"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Präsenz im Leiterkontext ändern","value":{"data":{"type":1}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"updateAdmission","line":63,"branch":{"file":"application/models/v2/Admission.php","line":72}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admission/ical_feed Persönlichen iCalendar-Link erzeugen Erzeugt einen signierten iCalendar-Feedlink für den aktuell angemeldeten Administrator. admin und assigned werden vom Handler gesetzt; der Client muss keine Nutzerauswahl senden. Die Rückgabe data.url enthält den Feedlink. - Operation-ID: post_v2_admission_ical_feed - Dokumentation: /docs/api/v2/operations/post-v2-admission-ical-feed-c9bb44a43c81/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admission/ical_feed' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/ical_feed"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/ical_feed" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admission_ical_feed","summary":"Persönlichen iCalendar-Link erzeugen","description":"Erzeugt einen signierten iCalendar-Feedlink für den aktuell angemeldeten Administrator. admin und assigned werden vom Handler gesetzt; der Client muss keine Nutzerauswahl senden. Die Rückgabe data.url enthält den Feedlink.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"url":"https://app.edoobox.com/v2/auth/i/c.ics?t=SYNTHETIC&n=demo"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Persönlichen iCalendar-Link erzeugen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"postAdmission","line":19,"branch":{"file":"application/models/v2/Admission.php","line":28}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admission/internal_documents Interne Angebotsdokumente im Leiterkontext lesen Prüft den Leiter-/Adminzugang auf filter.date und fragt danach nicht gelöschte Dateien des Kontos über den allgemeinen Dateihandler ab. Die Terminprüfung beschränkt die Dateiauswahl aktuell nicht automatisch auf das Angebot oder interne Dokumente. Für genau dessen interne Unterlagen zusätzlich explizite property/value-Filter auf resource_type=offer, resource_id mit der Angebots-ID und filetype=internalOfferFile senden. Custom-Exportdateien sind ausgeschlossen. - Operation-ID: get_v2_admission_internal_documents - Dokumentation: /docs/api/v2/operations/get-v2-admission-internal-documents-a4502123b82b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | ja | date ist für die Terminprüfung erforderlich. Für die gewünschte Dateiauswahl zusätzliche explizite Ressourcen-/Dateitypfilter setzen. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"date ist für die Terminprüfung erforderlich. Für die gewünschte Dateiauswahl zusätzliche explizite Ressourcen-/Dateitypfilter setzen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"date"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"date","value":"date_012345abcdef_123456"},{"property":"resource_type","value":"offer"},{"property":"resource_id","value":"offer_012345abcdef_123456"},{"property":"filetype","value":"internalOfferFile"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admission/internal_documents' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/internal_documents"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/internal_documents" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admission_internal_documents","summary":"Interne Angebotsdokumente im Leiterkontext lesen","description":"Prüft den Leiter-/Adminzugang auf filter.date und fragt danach nicht gelöschte Dateien des Kontos über den allgemeinen Dateihandler ab. Die Terminprüfung beschränkt die Dateiauswahl aktuell nicht automatisch auf das Angebot oder interne Dokumente. Für genau dessen interne Unterlagen zusätzlich explizite property/value-Filter auf resource_type=offer, resource_id mit der Angebots-ID und filetype=internalOfferFile senden. Custom-Exportdateien sind ausgeschlossen.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"date ist für die Terminprüfung erforderlich. Für die gewünschte Dateiauswahl zusätzliche explizite Ressourcen-/Dateitypfilter setzen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"date"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"date","value":"date_012345abcdef_123456"},{"property":"resource_type","value":"offer"},{"property":"resource_id","value":"offer_012345abcdef_123456"},{"property":"filetype","value":"internalOfferFile"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data ist standardmässig eine nach files-ID indizierte Sammlung; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"files_012345abcdef_123456":{"id":"files_012345abcdef_123456","filename":"Unterlagen.pdf","description":{"de":"Kursunterlagen"},"filetype":"internalOfferFile","resource_type":"offer","resource_id":"offer_012345abcdef_123456","last_change":"2026-10-22T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"string"}},{"type":"array","items":{}}]},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"const":false}]},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"string"}},{"type":"array","items":{}}]},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"const":false}]},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":["Quellseitig werden resource_type/resource_id/filetype als assoziative filter-Schlüssel ergänzt. API_DATA verarbeitet nur property/value-Einträge; diese vorbereiteten Schlüssel begrenzen die Liste daher nicht. Die Dokumentation behauptet keine zusätzliche Angebotsberechtigung für gelistete Dateien."]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"getAdmission","line":136,"branch":{"file":"application/models/v2/Admission.php","line":156}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admission/internal_documents Internes Dokument zu einem Leitertermin hochladen Lädt eine Datei für den Termin im separaten Multipartfeld date hoch, dessen Leiter-/Adminzugang zuerst geprüft wird. Ohne meta setzt Admission die Angebotszuordnung und internalOfferFile serverseitig. Wenn meta mit data gesendet wird, ersetzt Files diesen vorbereiteten Metadatenblock vollständig; dann müssen resource_type, resource_id und filetype vollständig und konsistent zum Angebot des Termins enthalten sein. Die Datei darf höchstens 20 MiB umfassen. - Operation-ID: post_v2_admission_internal_documents - Dokumentation: /docs/api/v2/operations/post-v2-admission-internal-documents-7dfb24bc9790/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admission/internal_documents' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/internal_documents"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/admission/internal_documents" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admission_internal_documents","summary":"Internes Dokument zu einem Leitertermin hochladen","description":"Lädt eine Datei für den Termin im separaten Multipartfeld date hoch, dessen Leiter-/Adminzugang zuerst geprüft wird. Ohne meta setzt Admission die Angebotszuordnung und internalOfferFile serverseitig. Wenn meta mit data gesendet wird, ersetzt Files diesen vorbereiteten Metadatenblock vollständig; dann müssen resource_type, resource_id und filetype vollständig und konsistent zum Angebot des Termins enthalten sein. Die Datei darf höchstens 20 MiB umfassen.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success sowie fachliche Statusfelder prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":{"x-edoobox-canonicalType":"string","description":"Text je unterstützter Sprache."},"description":"Sprachcode als Schlüssel, Text als Wert; nur unterstützte Sprachcodes werden gespeichert, normalisiert und auf 500 Zeichen begrenzt."},"meta":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"object","properties":{"data":{"type":"object","properties":{"resource_type":{"type":"string","enum":["admin","user","offer"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"filetype":{"type":"string","enum":["avatar","sign","image2","image3","internalOfferFile","externalOfferFile"]},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":{"x-edoobox-canonicalType":"string","description":"Text je unterstützter Sprache."},"description":"Sprachcode als Schlüssel, Text als Wert; nur unterstützte Sprachcodes werden gespeichert, normalisiert und auf 500 Zeichen begrenzt."},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["resource_type","resource_id","filetype"]}},"additionalProperties":true,"required":["data"]},"description":"Optionaler vollständiger Metadaten-Umschlag; date trotzdem zusätzlich als eigenes Formfeld senden."}},"additionalProperties":true,"required":["file","date"]},"examples":{"document":{"value":{"file":"","date":"date_012345abcdef_123456","meta":"{\"data\":{\"date\":\"date_012345abcdef_123456\",\"resource_type\":\"offer\",\"resource_id\":\"offer_012345abcdef_123456\",\"filetype\":\"internalOfferFile\",\"filename\":\"Unterlagen.pdf\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"postAdmission","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admission/internal_documents/files/{fileId}/download Internes Angebotsdokument herunterladen Prüft den Zugriff auf den Termin aus filter.date und lädt danach die Datei mit öffentlicher fileId über den allgemeinen Dateihandler. Der Dateihandler prüft Konto und Löschstatus; er schränkt den Download nicht zusätzlich auf das Angebot des Termins ein. Die Antwort enthält Dateibytes oder file_not_found. - Operation-ID: get_v2_admission_internal_documents_files_fileId_download - Dokumentation: /docs/api/v2/operations/get-v2-admission-internal-documents-files-fileid-download-af46da4c1ab3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fileId | path | ja | Opaque route segment accepted by this action; see source. | | filter | query | ja | JSON-Filterliste mit den angegebenen Selektoren. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fileId","in":"path","required":true,"description":"Opaque route segment accepted by this action; see source.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit den angegebenen Selektoren.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"date"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"date","value":"date_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_FILEID:?PATH_FILEID setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admission/internal_documents/files/'"${PATH_FILEID}"'/download' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/internal_documents/files/{fileId}/download"; path = path.replaceAll("{fileId}", encodeURIComponent(required("PATH_FILEID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/internal_documents/files/{fileId}/download" path = path.replace("{fileId}", urllib.parse.quote(os.environ["PATH_FILEID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admission_internal_documents_files_fileId_download","summary":"Internes Angebotsdokument herunterladen","description":"Prüft den Zugriff auf den Termin aus filter.date und lädt danach die Datei mit öffentlicher fileId über den allgemeinen Dateihandler. Der Dateihandler prüft Konto und Löschstatus; er schränkt den Download nicht zusätzlich auf das Angebot des Termins ein. Die Antwort enthält Dateibytes oder file_not_found.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fileId","in":"path","required":true,"description":"Opaque route segment accepted by this action; see source.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit den angegebenen Selektoren.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"date"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"date","value":"date_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Dateidownload mit Dateinamen in Content-Disposition; der Dateityp hängt von der hochgeladenen Datei ab. Fehlende oder ausgeschlossene Dateien liefern eine JSON-Fehlerantwort.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"application/json":{"example":{"success":true,"error":false,"data":{}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"getAdmission","line":140},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/admission/next_dates Nächste zugewiesene Termine laden Liest bis zu 50 zukünftige Termine ab drei Stunden vor jetzt für den angemeldeten Leiter. filter.date als parsebarer Datumswert beschränkt auf einen Kalendertag und hebt das 50er-Limit auf. display_all_dates=true/1/on/yes erweitert die Sicht nur bei Admin-ACL 2 oder 3 auf alle passenden Angebote des Kontos. - Operation-ID: get_v2_admission_next_dates - Dokumentation: /docs/api/v2/operations/get-v2-admission-next-dates-64a0a1e1f055/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | Optionaler filter.date ist ein von Carbon parsebarer Kalendertag, keine date-ID. | | display_all_dates | query | nein | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"Optionaler filter.date ist ein von Carbon parsebarer Kalendertag, keine date-ID.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date","value":"2026-11-02"}]}}},{"name":"display_all_dates","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"true, 1, on oder yes (Gross-/Kleinschreibung egal) erweitert die Sicht bei ACL 2/3."},"example":"true"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/admission/next_dates' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/next_dates"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admission/next_dates" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_admission_next_dates","summary":"Nächste zugewiesene Termine laden","description":"Liest bis zu 50 zukünftige Termine ab drei Stunden vor jetzt für den angemeldeten Leiter. filter.date als parsebarer Datumswert beschränkt auf einen Kalendertag und hebt das 50er-Limit auf. display_all_dates=true/1/on/yes erweitert die Sicht nur bei Admin-ACL 2 oder 3 auf alle passenden Angebote des Kontos.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"Optionaler filter.date ist ein von Carbon parsebarer Kalendertag, keine date-ID.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date","value":"2026-11-02"}]}}},{"name":"display_all_dates","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"true, 1, on oder yes (Gross-/Kleinschreibung egal) erweitert die Sicht bei ACL 2/3."},"example":"true"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"date_012345abcdef_123456","description":"Vormittag","date_start":"2026-11-02T09:00:00+01:00","date_end":"2026-11-02T12:00:00+01:00","offer":"offer_012345abcdef_123456","offer_name":"Einführungskurs","leader":[{"id":"admin_012345abcdef_123456","name":"Alex Beispiel"}],"place":false,"room":false,"color":"#336699"}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"description":{"type":"string"},"date_start":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"date_end":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"offer_name":{"type":"string"},"leader":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"place":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","examples":["place_012345abcdef_123456"]},"name":{"type":"string"},"shortcut":{"type":"string"},"street":{"type":"string"},"postcode":{"type":"string"},"town":{"type":"string"}},"additionalProperties":true},{"const":false}]},"room":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","examples":["room_012345abcdef_123456"]},"name":{"type":"string"},"place":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","examples":["place_012345abcdef_123456"]},"availability":{"type":"integer"}},"additionalProperties":true},{"const":false}]},"color":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"getAdmission","line":136,"branch":{"file":"application/models/v2/Admission.php","line":148}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/admission/pusherwss Echtzeitkanal für die Einlassansicht autorisieren Autorisiert eine Pusher-Socketverbindung für den kontospezifischen privaten Admission-Kanal. channel_name ist hier der öffentliche Angebotsbezeichner, aus dem der Server private-admission-{account}-{offer} aufbaut; socket_id stammt vom Pusher-Client. Beide Werte müssen als Formfelder gesendet werden. Die Antwort ist eine text/plain-Pusher-Signatur, kein API-Datenumschlag. - Operation-ID: post_v2_admission_pusherwss - Dokumentation: /docs/api/v2/operations/post-v2-admission-pusherwss-364480ea35b0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/admission/pusherwss' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admission/pusherwss"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/admission/pusherwss" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_admission_pusherwss","summary":"Echtzeitkanal für die Einlassansicht autorisieren","description":"Autorisiert eine Pusher-Socketverbindung für den kontospezifischen privaten Admission-Kanal. channel_name ist hier der öffentliche Angebotsbezeichner, aus dem der Server private-admission-{account}-{offer} aufbaut; socket_id stammt vom Pusher-Client. Beide Werte müssen als Formfelder gesendet werden. Die Antwort ist eine text/plain-Pusher-Signatur, kein API-Datenumschlag.","tags":["admission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Pusher socket_auth JSON-Text im MIME-Typ text/plain; kein API-Datenumschlag.","content":{"text/plain":{"schema":{"type":"string"},"example":"{\"auth\":\"SYNTHETIC_KEY:SYNTHETIC_SIGNATURE\"}"}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"channel_name":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"socket_id":{"type":"string"}},"additionalProperties":true,"required":["channel_name","socket_id"]},"example":{"channel_name":"offer_012345abcdef_123456","socket_id":"1234.5678"}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admission.php","method":"postAdmission","line":19,"branch":{"file":"application/models/v2/Admission.php","line":43}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admission","short":"adms","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/admissioncontrol Barcode prüfen und Präsenz setzen Prüft den Scan-Code samt Prüfsumme und verarbeitet validity mit ein bis fünf Prüfgruppen. Jede Gruppe enthält bis zu 20 Termine sowie behavior und attendance mit Normal- und Wiederholungsstatus. Eine optionale participant-ID löst mehrere Teilnahmen desselben Kunden auf; ohne eindeutige Zuordnung wird vor dem ersten Schreiben participant_ambiguous zurückgegeben. Ungültige/abgelaufene Codes und fachliche Prüfprobleme stehen auch bei error=false in data.error und data.validity[*].error. Ohne Pfad-Code muss id den Scan-Code enthalten, keine admissioncontrol-ID. - Operation-ID: put_v2_admissioncontrol - Dokumentation: /docs/api/v2/operations/put-v2-admissioncontrol-e742f10cb7c7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "validity": [ { "dates": [ "date_012345abcdef_123456" ], "behavior": {}, "attendance": { "set": { "type": 1 }, "alreadyset": { "check": true, "set": { "type": 2 } } } } ], "participant": "participant_012345abcdef_123456" }, "id": "DEMOX377" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/admissioncontrol' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admissioncontrol"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "validity": [ { "dates": [ "date_012345abcdef_123456" ], "behavior": {}, "attendance": { "set": { "type": 1 }, "alreadyset": { "check": true, "set": { "type": 2 } } } } ], "participant": "participant_012345abcdef_123456" }, "id": "DEMOX377" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admissioncontrol" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"validity\":[{\"dates\":[\"date_012345abcdef_123456\"],\"behavior\":{},\"attendance\":{\"set\":{\"type\":1},\"alreadyset\":{\"check\":true,\"set\":{\"type\":2}}}}],\"participant\":\"participant_012345abcdef_123456\"},\"id\":\"DEMOX377\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_admissioncontrol","summary":"Barcode prüfen und Präsenz setzen","description":"Prüft den Scan-Code samt Prüfsumme und verarbeitet validity mit ein bis fünf Prüfgruppen. Jede Gruppe enthält bis zu 20 Termine sowie behavior und attendance mit Normal- und Wiederholungsstatus. Eine optionale participant-ID löst mehrere Teilnahmen desselben Kunden auf; ohne eindeutige Zuordnung wird vor dem ersten Schreiben participant_ambiguous zurückgegeben. Ungültige/abgelaufene Codes und fachliche Prüfprobleme stehen auch bei error=false in data.error und data.validity[*].error. Ohne Pfad-Code muss id den Scan-Code enthalten, keine admissioncontrol-ID.","tags":["admissioncontrol"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.error und validity[*].error müssen auch bei error=false geprüft werden; pro Angebot und Termin sind Ergebnisobjekte mit booking, attendance und set möglich.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"code":"DEMOX377","error":[],"barcode":{"user":"user_012345abcdef_123456","status":true},"validity":[{"data":[],"error":[]}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"code":{"type":["string","boolean"]},"error":{"type":"array","items":{"type":"string"}},"barcode":{"type":"object","properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"status":{"type":"boolean"},"user_name":{"type":"string"},"pricecategory_name":{"type":"string"}},"additionalProperties":true},"validity":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"data":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"lesson_booking":{"type":"boolean"},"date":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"waiting_list":{"type":"boolean"},"time":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}},"additionalProperties":true},{"const":false}]},"nobooking":{"type":"boolean"},"nobookingdate":{"type":"boolean"},"alreadyset":{"type":"boolean"},"attendance":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"type":{"type":"integer"}},"additionalProperties":true},{"const":false}]},"set":{"type":["integer","boolean"]}},"additionalProperties":true}}},"additionalProperties":true},"items":{}},"error":{"type":"array","items":{"type":"string"}},"require":{"type":"object","properties":{},"additionalProperties":true},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant_candidates":{"type":"array","items":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"data":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"lesson_booking":{"type":"boolean"},"date":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"waiting_list":{"type":"boolean"},"time":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}},"additionalProperties":true},{"const":false}]},"nobooking":{"type":"boolean"},"nobookingdate":{"type":"boolean"},"alreadyset":{"type":"boolean"},"attendance":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"type":{"type":"integer"}},"additionalProperties":true},{"const":false}]},"set":{"type":["integer","boolean"]}},"additionalProperties":true}}},"additionalProperties":true},"items":{}},"error":{"type":"array","items":{"type":"string"}},"require":{"type":"object","properties":{},"additionalProperties":true},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant_candidates":{"type":"array","items":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}}},"additionalProperties":true}}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"validity":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"dates":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minItems":1,"maxItems":20},{"type":"object","additionalProperties":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minProperties":1,"maxProperties":20}]},"behavior":{"type":["object","array"],"properties":{"existing":{},"nobooking":{},"nobookingdate":{}},"additionalProperties":true,"items":{},"description":"Der Container ist Pflicht. Die genannten Optionen werden eingelesen; die aktuelle Implementierung meldet bestehende/fehlende Buchungen unabhängig von diesen Werten."},"attendance":{"type":"object","properties":{"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]},"alreadyset":{"type":"object","properties":{"check":{"x-edoobox-canonicalType":"boolean","description":"Mit bool normalisiert; ohne check wird true verwendet."},"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]}},"additionalProperties":true,"required":["set"]}},"additionalProperties":true,"required":["set","alreadyset"]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]}},"additionalProperties":true,"required":["dates","behavior","attendance"]},"minItems":1,"maxItems":5},{"type":"object","additionalProperties":{"type":"object","properties":{"dates":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minItems":1,"maxItems":20},{"type":"object","additionalProperties":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minProperties":1,"maxProperties":20}]},"behavior":{"type":["object","array"],"properties":{"existing":{},"nobooking":{},"nobookingdate":{}},"additionalProperties":true,"items":{},"description":"Der Container ist Pflicht. Die genannten Optionen werden eingelesen; die aktuelle Implementierung meldet bestehende/fehlende Buchungen unabhängig von diesen Werten."},"attendance":{"type":"object","properties":{"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]},"alreadyset":{"type":"object","properties":{"check":{"x-edoobox-canonicalType":"boolean","description":"Mit bool normalisiert; ohne check wird true verwendet."},"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]}},"additionalProperties":true,"required":["set"]}},"additionalProperties":true,"required":["set","alreadyset"]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]}},"additionalProperties":true,"required":["dates","behavior","attendance"]},"minProperties":1,"maxProperties":5}]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]},"ignore_expired_code":{"x-edoobox-canonicalType":"boolean","description":"Ein truthy-Wert erlaubt die weitere Prüfung trotz abgelaufenem Barcode; data.error enthält weiterhin code_expired."}},"additionalProperties":true,"required":["validity"]},"id":{"type":"string","description":"Gesamter Scan-Code einschliesslich X-Prüfsumme; keine v2-ID.","examples":["DEMOX377"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Barcode prüfen und Präsenz setzen","value":{"data":{"validity":[{"dates":["date_012345abcdef_123456"],"behavior":{},"attendance":{"set":{"type":1},"alreadyset":{"check":true,"set":{"type":2}}}}],"participant":"participant_012345abcdef_123456"},"id":"DEMOX377"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admissioncontrol.php","method":"updateAdmissioncontrol","line":39},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admissioncontrol","short":"adco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/admissioncontrol/{code} Barcode prüfen und Präsenz setzen Prüft den Scan-Code samt Prüfsumme und verarbeitet validity mit ein bis fünf Prüfgruppen. Jede Gruppe enthält bis zu 20 Termine sowie behavior und attendance mit Normal- und Wiederholungsstatus. Eine optionale participant-ID löst mehrere Teilnahmen desselben Kunden auf; ohne eindeutige Zuordnung wird vor dem ersten Schreiben participant_ambiguous zurückgegeben. Ungültige/abgelaufene Codes und fachliche Prüfprobleme stehen auch bei error=false in data.error und data.validity[*].error. Ohne Pfad-Code muss id den Scan-Code enthalten, keine admissioncontrol-ID. - Operation-ID: put_v2_admissioncontrol_code - Dokumentation: /docs/api/v2/operations/put-v2-admissioncontrol-code-3c08dffda9a6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | path | ja | Gesamter Scan-Code einschliesslich X-Prüfsumme. | | language | header | nein | | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Gesamter Scan-Code einschliesslich X-Prüfsumme."},{"name":"language","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CODE:?PATH_CODE setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "validity": [ { "dates": [ "date_012345abcdef_123456" ], "behavior": {}, "attendance": { "set": { "type": 1 }, "alreadyset": { "check": true, "set": { "type": 2 } } } } ], "participant": "participant_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/admissioncontrol/'"${PATH_CODE}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/admissioncontrol/{code}"; path = path.replaceAll("{code}", encodeURIComponent(required("PATH_CODE"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "validity": [ { "dates": [ "date_012345abcdef_123456" ], "behavior": {}, "attendance": { "set": { "type": 1 }, "alreadyset": { "check": true, "set": { "type": 2 } } } } ], "participant": "participant_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/admissioncontrol/{code}" path = path.replace("{code}", urllib.parse.quote(os.environ["PATH_CODE"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"validity\":[{\"dates\":[\"date_012345abcdef_123456\"],\"behavior\":{},\"attendance\":{\"set\":{\"type\":1},\"alreadyset\":{\"check\":true,\"set\":{\"type\":2}}}}],\"participant\":\"participant_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_admissioncontrol_code","summary":"Barcode prüfen und Präsenz setzen","description":"Prüft den Scan-Code samt Prüfsumme und verarbeitet validity mit ein bis fünf Prüfgruppen. Jede Gruppe enthält bis zu 20 Termine sowie behavior und attendance mit Normal- und Wiederholungsstatus. Eine optionale participant-ID löst mehrere Teilnahmen desselben Kunden auf; ohne eindeutige Zuordnung wird vor dem ersten Schreiben participant_ambiguous zurückgegeben. Ungültige/abgelaufene Codes und fachliche Prüfprobleme stehen auch bei error=false in data.error und data.validity[*].error. Ohne Pfad-Code muss id den Scan-Code enthalten, keine admissioncontrol-ID.","tags":["admissioncontrol"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"},"description":"Gesamter Scan-Code einschliesslich X-Prüfsumme."},{"name":"language","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.error und validity[*].error müssen auch bei error=false geprüft werden; pro Angebot und Termin sind Ergebnisobjekte mit booking, attendance und set möglich.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"code":"DEMOX377","error":[],"barcode":{"user":"user_012345abcdef_123456","status":true},"validity":[{"data":[],"error":[]}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"code":{"type":["string","boolean"]},"error":{"type":"array","items":{"type":"string"}},"barcode":{"type":"object","properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"status":{"type":"boolean"},"user_name":{"type":"string"},"pricecategory_name":{"type":"string"}},"additionalProperties":true},"validity":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"data":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"lesson_booking":{"type":"boolean"},"date":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"waiting_list":{"type":"boolean"},"time":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}},"additionalProperties":true},{"const":false}]},"nobooking":{"type":"boolean"},"nobookingdate":{"type":"boolean"},"alreadyset":{"type":"boolean"},"attendance":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"type":{"type":"integer"}},"additionalProperties":true},{"const":false}]},"set":{"type":["integer","boolean"]}},"additionalProperties":true}}},"additionalProperties":true},"items":{}},"error":{"type":"array","items":{"type":"string"}},"require":{"type":"object","properties":{},"additionalProperties":true},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant_candidates":{"type":"array","items":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"data":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"lesson_booking":{"type":"boolean"},"date":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"waiting_list":{"type":"boolean"},"time":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}},"additionalProperties":true},{"const":false}]},"nobooking":{"type":"boolean"},"nobookingdate":{"type":"boolean"},"alreadyset":{"type":"boolean"},"attendance":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"type":{"type":"integer"}},"additionalProperties":true},{"const":false}]},"set":{"type":["integer","boolean"]}},"additionalProperties":true}}},"additionalProperties":true},"items":{}},"error":{"type":"array","items":{"type":"string"}},"require":{"type":"object","properties":{},"additionalProperties":true},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant_candidates":{"type":"array","items":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}}},"additionalProperties":true}}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"validity":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"dates":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minItems":1,"maxItems":20},{"type":"object","additionalProperties":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minProperties":1,"maxProperties":20}]},"behavior":{"type":["object","array"],"properties":{"existing":{},"nobooking":{},"nobookingdate":{}},"additionalProperties":true,"items":{},"description":"Der Container ist Pflicht. Die genannten Optionen werden eingelesen; die aktuelle Implementierung meldet bestehende/fehlende Buchungen unabhängig von diesen Werten."},"attendance":{"type":"object","properties":{"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]},"alreadyset":{"type":"object","properties":{"check":{"x-edoobox-canonicalType":"boolean","description":"Mit bool normalisiert; ohne check wird true verwendet."},"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]}},"additionalProperties":true,"required":["set"]}},"additionalProperties":true,"required":["set","alreadyset"]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]}},"additionalProperties":true,"required":["dates","behavior","attendance"]},"minItems":1,"maxItems":5},{"type":"object","additionalProperties":{"type":"object","properties":{"dates":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minItems":1,"maxItems":20},{"type":"object","additionalProperties":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"minProperties":1,"maxProperties":20}]},"behavior":{"type":["object","array"],"properties":{"existing":{},"nobooking":{},"nobookingdate":{}},"additionalProperties":true,"items":{},"description":"Der Container ist Pflicht. Die genannten Optionen werden eingelesen; die aktuelle Implementierung meldet bestehende/fehlende Buchungen unabhängig von diesen Werten."},"attendance":{"type":"object","properties":{"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]},"alreadyset":{"type":"object","properties":{"check":{"x-edoobox-canonicalType":"boolean","description":"Mit bool normalisiert; ohne check wird true verwendet."},"set":{"type":"object","properties":{"type":{"x-edoobox-canonicalType":"integer","description":"Mit PHP int normalisiert; danach muss 1 bis 100 erreicht werden. Ein Integer ist kanonisch; numerischer Text wird ebenso verarbeitet."}},"additionalProperties":true,"required":["type"]}},"additionalProperties":true,"required":["set"]}},"additionalProperties":true,"required":["set","alreadyset"]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]}},"additionalProperties":true,"required":["dates","behavior","attendance"]},"minProperties":1,"maxProperties":5}]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]},"ignore_expired_code":{"x-edoobox-canonicalType":"boolean","description":"Ein truthy-Wert erlaubt die weitere Prüfung trotz abgelaufenem Barcode; data.error enthält weiterhin code_expired."}},"additionalProperties":true,"required":["validity"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Barcode prüfen und Präsenz setzen","value":{"data":{"validity":[{"dates":["date_012345abcdef_123456"],"behavior":{},"attendance":{"set":{"type":1},"alreadyset":{"check":true,"set":{"type":2}}}}],"participant":"participant_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Admissioncontrol.php","method":"updateAdmissioncontrol","line":39},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"admissioncontrol","short":"adco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/ai KI-Textvorlagen oder Provider-Verbindung prüfen Ohne action liefert der Endpunkt die verfügbaren Aktionen. action=templates gibt den Katalog der Textbearbeitungs-Vorlagen zurück; action=test_connection ruft die Modellliste des serverseitig konfigurierten Providers ab. Die Verbindungsprüfung löst eine externe Anfrage aus und ist kein reiner lokaler Lesezugriff. Die Ergebnisse liegen als templates/count bzw. connection_test auf oberster JSON-Ebene, nicht in data. - Operation-ID: get_v2_ai - Dokumentation: /docs/api/v2/operations/get-v2-ai-12b2eb011b7f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | action | query | nein | Optionaler Abrufmodus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"action","in":"query","required":false,"description":"Optionaler Abrufmodus.","schema":{"type":"string","enum":["templates","test_connection"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/ai' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/ai"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/ai" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_ai","summary":"KI-Textvorlagen oder Provider-Verbindung prüfen","description":"Ohne action liefert der Endpunkt die verfügbaren Aktionen. action=templates gibt den Katalog der Textbearbeitungs-Vorlagen zurück; action=test_connection ruft die Modellliste des serverseitig konfigurierten Providers ab. Die Verbindungsprüfung löst eine externe Anfrage aus und ist kein reiner lokaler Lesezugriff. Die Ergebnisse liegen als templates/count bzw. connection_test auf oberster JSON-Ebene, nicht in data.","tags":["ai"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"action","in":"query","required":false,"description":"Optionaler Abrufmodus.","schema":{"type":"string","enum":["templates","test_connection"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Aktionsabhängige Felder auf oberster Ebene. Der Legacy-Handler setzt kein error=false; der globale success-Wert allein ist hier daher keine zuverlässige Erfolgsaussage.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"templates":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"additionalProperties":true}},"count":{"type":"integer"},"connection_test":{"type":"object","properties":{"error":{"type":["boolean","string"]},"models_count":{"type":"integer"},"available_models":{"type":"array","items":{"type":"string"}},"error_message":{"type":"string"}},"additionalProperties":true},"available_actions":{"type":"object","additionalProperties":{"type":"string"}},"usage":{"type":"string"}},"additionalProperties":true},"example":{"success":false,"templates":{"text_optimize":{"name":"Text-Inhalt optimieren und verbessern","description":"Text optimieren"}},"count":1}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Ai.php","method":"getAi","line":165},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"ai","short":"ai","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/ai Text mit KI-Vorlage erzeugen, optimieren oder übersetzen Verarbeitet den Inhalt mit einer serverseitigen KI-Textvorlage. template_key und prompt sind Pflicht; prompt enthält content, title, description und/oder requirements. options.streaming=false fordert eine abschließende JSON-Antwort mit content, truncated und reason an. Standardmäßig schreibt der Legacy-Pfad data:-gerahmte Streamereignisse und protokolliert Provider-Nutzung; er speichert den erzeugten Text nicht als Angebot oder Vorlage. Der Inhalt wird vor dem Provideraufruf gefiltert. Dieser Legacy-Endpunkt ersetzt nicht den Vorschlag-/Freigabeablauf von aiassistant. Der Streaming-Zweig setzt zunächst Content-Type text/plain; charset=utf-8 und schreibt jeweils data: gefolgt von einem JSON-Ereignis und zwei Zeilenumbrüchen. Danach kehrt die Methode in den zentralen API_OUTPUT/respondOK-Pfad zurück, der einen abschließenden JSON-Umschlag schreibt und application/json zu setzen versucht. Bereits gesendete Header/Bytes können dadurch nicht zurückgenommen werden: die Gesamtantwort kann aus Frames und angehängtem JSON bestehen und darf nicht als durchgehend gültiges JSON geparst werden. Server-/Proxy-Pufferung beeinflusst die sichtbaren Header und Chunk-Grenzen; Optionen und erzeugte Abbruchereignisse bestimmen den Inhalt. content-Ereignisse liefern einzelne Textstücke; final wird bei Timeout oder max_tokens mit truncated=true, reason und dem bis dahin gesammelten content geschrieben; end beendet den Ereignisteil. Der zurückgegebene erfolgreiche Streaming-Array enthält streaming=true und content, setzt selbst aber kein error=false. Der zentrale success-Wert kann deshalb false sein, obwohl der Ereignisteil erzeugt wurde; immer den dokumentierten Modus und dessen Felder auswerten. - Operation-ID: post_v2_ai - Dokumentation: /docs/api/v2/operations/post-v2-ai-5df399c43154/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "template_key": "text_optimize", "prompt": { "content": "Willkommen zu unserem Kurs." }, "options": { "streaming": false, "output_size": "short" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/ai' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/ai"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "template_key": "text_optimize", "prompt": { "content": "Willkommen zu unserem Kurs." }, "options": { "streaming": false, "output_size": "short" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/ai" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"template_key\":\"text_optimize\",\"prompt\":{\"content\":\"Willkommen zu unserem Kurs.\"},\"options\":{\"streaming\":false,\"output_size\":\"short\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_ai","summary":"Text mit KI-Vorlage erzeugen, optimieren oder übersetzen","description":"Verarbeitet den Inhalt mit einer serverseitigen KI-Textvorlage. template_key und prompt sind Pflicht; prompt enthält content, title, description und/oder requirements. options.streaming=false fordert eine abschließende JSON-Antwort mit content, truncated und reason an. Standardmäßig schreibt der Legacy-Pfad data:-gerahmte Streamereignisse und protokolliert Provider-Nutzung; er speichert den erzeugten Text nicht als Angebot oder Vorlage. Der Inhalt wird vor dem Provideraufruf gefiltert. Dieser Legacy-Endpunkt ersetzt nicht den Vorschlag-/Freigabeablauf von aiassistant. Der Streaming-Zweig setzt zunächst Content-Type text/plain; charset=utf-8 und schreibt jeweils data: gefolgt von einem JSON-Ereignis und zwei Zeilenumbrüchen. Danach kehrt die Methode in den zentralen API_OUTPUT/respondOK-Pfad zurück, der einen abschließenden JSON-Umschlag schreibt und application/json zu setzen versucht. Bereits gesendete Header/Bytes können dadurch nicht zurückgenommen werden: die Gesamtantwort kann aus Frames und angehängtem JSON bestehen und darf nicht als durchgehend gültiges JSON geparst werden. Server-/Proxy-Pufferung beeinflusst die sichtbaren Header und Chunk-Grenzen; Optionen und erzeugte Abbruchereignisse bestimmen den Inhalt. content-Ereignisse liefern einzelne Textstücke; final wird bei Timeout oder max_tokens mit truncated=true, reason und dem bis dahin gesammelten content geschrieben; end beendet den Ereignisteil. Der zurückgegebene erfolgreiche Streaming-Array enthält streaming=true und content, setzt selbst aber kein error=false. Der zentrale success-Wert kann deshalb false sein, obwohl der Ereignisteil erzeugt wurde; immer den dokumentierten Modus und dessen Felder auswerten.","tags":["ai"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bei streaming=false oberste JSON-Felder content/truncated/reason; Legacy-Streaming schreibt data:-Frames und kann danach eine API-JSON-Antwort anhängen.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"content":{"type":["string","null"]},"truncated":{"type":"boolean"},"reason":{"type":["string","null"]},"error_message":{"type":"string"},"streaming":{"type":"boolean"}},"additionalProperties":true},"example":{"success":true,"error":false,"content":"Herzlich willkommen zu unserem Kurs.","truncated":false,"reason":null}},"text/plain":{"schema":{"type":"string"},"example":"data: {\"type\":\"content\",\"content\":\"Herzlich willkommen.\"}\n\ndata: {\"type\":\"end\"}\n\n{\"success\":false,\"streaming\":true,\"truncated\":false,\"reason\":null,\"content\":\"Herzlich willkommen.\"}","description":"Legacy-Stream mit data:-JSON-Frames (content, optional final, end), gefolgt vom zentralen JSON-Abschluss. Der tatsächlich bereits gesendete Content-Type kann text/plain bleiben."}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"template_key":{"enum":["event_description_create","text_optimize","event_description_bootstrap","email_template_optimize","pdf_template_optimize","modern_event_description","translate"]},"prompt":{"type":"object","properties":{"content":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"requirements":{"type":"string"}},"additionalProperties":true},"options":{"type":"object","properties":{"streaming":{"type":"boolean"},"use_cache":{"type":"boolean"},"response_profile":{"type":"string","description":"speed, balanced oder quality; unbekannte Werte fallen auf das Standardprofil zurück."},"output_size":{"type":"string","description":"short, medium oder long; unbekannte Werte werden normalisiert."},"target_language":{"type":"string"},"custom_instructions":{"type":"string"},"model":{"type":"string"},"max_tokens":{"type":["integer","string"]},"retry_max_tokens":{"type":["integer","string"]},"temperature":{"type":["number","string"]},"resource_id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true}},"additionalProperties":true,"required":["template_key","prompt"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Text mit KI-Vorlage erzeugen, optimieren oder übersetzen","value":{"data":{"template_key":"text_optimize","prompt":{"content":"Willkommen zu unserem Kurs."},"options":{"streaming":false,"output_size":"short"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Ai.php","method":"postAi","line":25,"branch":{"file":"application/models/v2/Ai.php","line":46}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"ai","short":"ai","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/ai/list/demo_pusherevent Abschlussereignis für KI-Übersetzung senden Sendet das Konto-Pusher-Ereignis resource=offer, method=ai_translation_end mit der übergebenen öffentlichen offer_id und dem festen Anzeigenamen Unnamed Offer. Führt keine Übersetzung durch. data.offer_id liefert den Ereignisbezug; im Handler wird es ohne weitere Vorhandenheitsprüfung gelesen. Ohne Eingabefeld wird im Legacy-Handler ein leerer/null-Ereignisbezug gelesen; es gibt dafür keine eigene Pflichtfeldvalidierung. - Operation-ID: post_v2_ai_list_demo_pusherevent - Dokumentation: /docs/api/v2/operations/post-v2-ai-list-demo-pusherevent-6e035fdd7b28/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer_id": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/ai/list/demo_pusherevent' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/ai/list/demo_pusherevent"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer_id": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/ai/list/demo_pusherevent" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer_id\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_ai_list_demo_pusherevent","summary":"Abschlussereignis für KI-Übersetzung senden","description":"Sendet das Konto-Pusher-Ereignis resource=offer, method=ai_translation_end mit der übergebenen öffentlichen offer_id und dem festen Anzeigenamen Unnamed Offer. Führt keine Übersetzung durch. data.offer_id liefert den Ereignisbezug; im Handler wird es ohne weitere Vorhandenheitsprüfung gelesen. Ohne Eingabefeld wird im Legacy-Handler ein leerer/null-Ereignisbezug gelesen; es gibt dafür keine eigene Pflichtfeldvalidierung.","tags":["ai"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Oberste Felder error=false, success=true und status=pusher event sent; kein data-Objekt.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"success":{"type":"boolean"},"status":{"type":"string"}},"additionalProperties":true},"example":{"error":false,"success":true,"status":"pusher event sent"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Abschlussereignis für KI-Übersetzung senden","value":{"data":{"offer_id":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Ai.php","method":"postAi","line":25,"branch":{"file":"application/models/v2/Ai.php","line":34}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"ai","short":"ai","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/ai/list/offertranslate Angebot in die aktiven Kontosprachen übersetzen Übersetzt die Angebotstexte aus der Standardsprache in alle aktiven Kontosprachen und speichert die Übersetzungen. offer_id ist Pflicht und muss auf ein Angebot des Kontos verweisen. Maximal sechs aktive Sprachen und höchstens 10000 Bytes Beschreibung sind erlaubt; pro Konto läuft höchstens ein Übersetzungsauftrag. options wird angenommen, im aktuellen translateOffer-Pfad aber nicht ausgewertet. Nach früher JSON-Bestätigung erfolgt die Verarbeitung weiter; das Pusher-Ereignis ai_translation_end signalisiert das Ende. Die Bestätigung ist kein Nachweis erfolgreich gespeicherter Übersetzungen. - Operation-ID: post_v2_ai_list_offertranslate - Dokumentation: /docs/api/v2/operations/post-v2-ai-list-offertranslate-ff9aef43c75c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer_id": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/ai/list/offertranslate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/ai/list/offertranslate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer_id": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/ai/list/offertranslate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer_id\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_ai_list_offertranslate","summary":"Angebot in die aktiven Kontosprachen übersetzen","description":"Übersetzt die Angebotstexte aus der Standardsprache in alle aktiven Kontosprachen und speichert die Übersetzungen. offer_id ist Pflicht und muss auf ein Angebot des Kontos verweisen. Maximal sechs aktive Sprachen und höchstens 10000 Bytes Beschreibung sind erlaubt; pro Konto läuft höchstens ein Übersetzungsauftrag. options wird angenommen, im aktuellen translateOffer-Pfad aber nicht ausgewertet. Nach früher JSON-Bestätigung erfolgt die Verarbeitung weiter; das Pusher-Ereignis ai_translation_end signalisiert das Ende. Die Bestätigung ist kein Nachweis erfolgreich gespeicherter Übersetzungen.","tags":["ai"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Frühe Annahmebestätigung mit öffentlicher Angebots-ID und translate_run=true; Abschluss über Pusher.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","translate_run":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"translate_run":{"type":"boolean"}},"additionalProperties":true,"required":["id","translate_run"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"options":{"type":"object","properties":{},"additionalProperties":true,"description":"Optionales Optionsobjekt; in dieser Implementierung ohne Wirkung."}},"additionalProperties":true,"required":["offer_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Angebot in die aktiven Kontosprachen übersetzen","value":{"data":{"offer_id":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Ai.php","method":"postAi","line":25,"branch":{"file":"application/models/v2/Ai.php","line":46}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"ai","short":"ai","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/ai/translate/offertranslate Angebot in die aktiven Kontosprachen übersetzen Übersetzt die Angebotstexte aus der Standardsprache in alle aktiven Kontosprachen und speichert die Übersetzungen. offer_id ist Pflicht und muss auf ein Angebot des Kontos verweisen. Maximal sechs aktive Sprachen und höchstens 10000 Bytes Beschreibung sind erlaubt; pro Konto läuft höchstens ein Übersetzungsauftrag. options wird angenommen, im aktuellen translateOffer-Pfad aber nicht ausgewertet. Nach früher JSON-Bestätigung erfolgt die Verarbeitung weiter; das Pusher-Ereignis ai_translation_end signalisiert das Ende. Die Bestätigung ist kein Nachweis erfolgreich gespeicherter Übersetzungen. - Operation-ID: post_v2_ai_translate_offertranslate - Dokumentation: /docs/api/v2/operations/post-v2-ai-translate-offertranslate-b93871a0376e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer_id": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/ai/translate/offertranslate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/ai/translate/offertranslate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer_id": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/ai/translate/offertranslate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer_id\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_ai_translate_offertranslate","summary":"Angebot in die aktiven Kontosprachen übersetzen","description":"Übersetzt die Angebotstexte aus der Standardsprache in alle aktiven Kontosprachen und speichert die Übersetzungen. offer_id ist Pflicht und muss auf ein Angebot des Kontos verweisen. Maximal sechs aktive Sprachen und höchstens 10000 Bytes Beschreibung sind erlaubt; pro Konto läuft höchstens ein Übersetzungsauftrag. options wird angenommen, im aktuellen translateOffer-Pfad aber nicht ausgewertet. Nach früher JSON-Bestätigung erfolgt die Verarbeitung weiter; das Pusher-Ereignis ai_translation_end signalisiert das Ende. Die Bestätigung ist kein Nachweis erfolgreich gespeicherter Übersetzungen.","tags":["ai"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Frühe Annahmebestätigung mit öffentlicher Angebots-ID und translate_run=true; Abschluss über Pusher.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","translate_run":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"translate_run":{"type":"boolean"}},"additionalProperties":true,"required":["id","translate_run"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"options":{"type":"object","properties":{},"additionalProperties":true,"description":"Optionales Optionsobjekt; in dieser Implementierung ohne Wirkung."}},"additionalProperties":true,"required":["offer_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Angebot in die aktiven Kontosprachen übersetzen","value":{"data":{"offer_id":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Ai.php","line":46,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Ai.php","method":"postAi","line":25,"branch":{"file":"application/models/v2/Ai.php","line":46}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"ai","short":"ai","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/aiassistant KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert Aktivierung, Providerstatus, Tokenbudget, Kaufverfügbarkeit und Berechtigungsanzeigen. Initialisiert bei Bedarf Konto-Einstellungen und Monatsverbrauch und gleicht serverseitig bestätigte Zahlungen ab; dadurch kann bezahltes Token-Guthaben gutgeschrieben oder überprüft werden. Gibt keinen OpenAI-Schlüssel oder sonstige Provider-Zugangsdaten zurück. - Operation-ID: get_v2_aiassistant - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-83e778394f1a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant","summary":"KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert Aktivierung, Providerstatus, Tokenbudget, Kaufverfügbarkeit und Berechtigungsanzeigen. Initialisiert bei Bedarf Konto-Einstellungen und Monatsverbrauch und gleicht serverseitig bestätigte Zahlungen ab; dadurch kann bezahltes Token-Guthaben gutgeschrieben oder überprüft werden. Gibt keinen OpenAI-Schlüssel oder sonstige Provider-Zugangsdaten zurück.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Statusobjekt mit enabled, provider, quota, byok, purchase, retention_days und permissions.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"enabled":true,"provider":{"mode":"platform","configured":true,"simulation":false},"quota":{"month":"2026-10","free_limit":100000,"input_tokens":0,"output_tokens":0,"total_tokens":0,"reserved_tokens":0,"byok_input_tokens":0,"byok_output_tokens":0,"paid_balance_tokens":0,"paid_reserved_tokens":0,"paid_available_tokens":0,"free_remaining_tokens":100000,"remaining_tokens":100000,"payment_review_required":false},"byok":{"configured":false,"scope":"account"},"purchase":{"available":false,"route_name":"system.accounting"},"retention_days":30,"permissions":{"can_configure":true,"can_purchase":false,"can_activate":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"provider":{"type":"object","properties":{"mode":{"enum":["platform","byok"]},"configured":{"type":"boolean"},"simulation":{"type":"boolean"}},"additionalProperties":true},"quota":{"type":"object","properties":{"free_limit":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"total_tokens":{"type":"integer"},"reserved_tokens":{"type":"integer"},"byok_input_tokens":{"type":"integer"},"byok_output_tokens":{"type":"integer"},"paid_balance_tokens":{"type":"integer"},"paid_reserved_tokens":{"type":"integer"},"paid_available_tokens":{"type":"integer"},"free_remaining_tokens":{"type":"integer"},"remaining_tokens":{"type":"integer"},"month":{"type":"string"},"payment_review_required":{"type":"boolean"}},"additionalProperties":true},"byok":{"type":"object","properties":{"configured":{"type":"boolean"},"scope":{"const":"account"}},"additionalProperties":true},"purchase":{"type":"object","properties":{"available":{"type":"boolean"},"route_name":{"const":"system.accounting"}},"additionalProperties":true},"retention_days":{"type":"integer"},"permissions":{"type":"object","properties":{"can_configure":{"type":"boolean"},"can_purchase":{"type":"boolean"},"can_activate":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/aiassistant/artifacts/{artifactId} KI-Artefakt prüfen oder geschützt herunterladen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Ohne download=1 werden nur Metadaten geliefert. Mit download=1 werden Datei-Bytes mit private,no-store und Download-Headern gestreamt; Rechte werden vor und während gewöhnlicher Downloads erneut geprüft. Nur Artefakte nicht abgebrochener, erfolgreicher oder awaiting_approval-Läufe sind lesbar. Sensible Artefakte sind verschlüsselte einmalige Benutzerdownloads: der Download verbraucht das Artefakt dauerhaft und darf niemals von einem Modell oder automatisch abgerufen werden. X-Edoobox-MCP-Consumer:model wird bei sensiblen Downloads explizit abgelehnt. Ein fehlgeschlagener oder abgebrochener sensibler Download ist nicht automatisch wiederholbar. - Operation-ID: get_v2_aiassistant_artifacts_artifactId - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-artifacts-artifactid-d2801fa34644/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | artifactId | path | ja | Öffentliche, an Konto und Administrator gebundene aiassistantartifact-ID. | | download | query | nein | Nur exakt 1 startet den Download; sonst Metadaten. Sensible Einmaldateien ausschließlich durch expliziten Benutzerdownload. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-MCP-Consumer | header | nein | Kennzeichnet Modellzugriff; sensible Downloads sind dafür verboten. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"artifactId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantartifact-ID.","schema":{"type":"string","pattern":"^aiassistantartifact_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"download","in":"query","required":false,"description":"Nur exakt 1 startet den Download; sonst Metadaten. Sensible Einmaldateien ausschließlich durch expliziten Benutzerdownload.","schema":{"type":"string","enum":["0","1"],"default":"0"}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-MCP-Consumer","in":"header","required":false,"schema":{"type":"string","enum":["model"]},"description":"Kennzeichnet Modellzugriff; sensible Downloads sind dafür verboten."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ARTIFACTID:?PATH_ARTIFACTID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/artifacts/'"${PATH_ARTIFACTID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/artifacts/{artifactId}"; path = path.replaceAll("{artifactId}", encodeURIComponent(required("PATH_ARTIFACTID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/artifacts/{artifactId}" path = path.replace("{artifactId}", urllib.parse.quote(os.environ["PATH_ARTIFACTID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_artifacts_artifactId","summary":"KI-Artefakt prüfen oder geschützt herunterladen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Ohne download=1 werden nur Metadaten geliefert. Mit download=1 werden Datei-Bytes mit private,no-store und Download-Headern gestreamt; Rechte werden vor und während gewöhnlicher Downloads erneut geprüft. Nur Artefakte nicht abgebrochener, erfolgreicher oder awaiting_approval-Läufe sind lesbar. Sensible Artefakte sind verschlüsselte einmalige Benutzerdownloads: der Download verbraucht das Artefakt dauerhaft und darf niemals von einem Modell oder automatisch abgerufen werden. X-Edoobox-MCP-Consumer:model wird bei sensiblen Downloads explizit abgelehnt. Ein fehlgeschlagener oder abgebrochener sensibler Download ist nicht automatisch wiederholbar.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"artifactId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantartifact-ID.","schema":{"type":"string","pattern":"^aiassistantartifact_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"download","in":"query","required":false,"description":"Nur exakt 1 startet den Download; sonst Metadaten. Sensible Einmaldateien ausschließlich durch expliziten Benutzerdownload.","schema":{"type":"string","enum":["0","1"],"default":"0"}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-MCP-Consumer","in":"header","required":false,"schema":{"type":"string","enum":["model"]},"description":"Kennzeichnet Modellzugriff; sensible Downloads sind dafür verboten."}],"responses":{"200":{"description":"Metadaten oder bei download=1 direkte Datei-Bytes; sensible Dateien können nur einmal vom Benutzer heruntergeladen werden.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantartifact_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"name":{"type":"string"},"mime_type":{"type":"string"},"size":{"type":"integer"},"sensitive":{"type":"boolean"},"one_time":{"type":"boolean"}},"additionalProperties":true,"required":["id","name","mime_type","size"]},"error_code":{"type":"string"}},"additionalProperties":true},"example":{"success":true,"error":false,"data":{"id":"aiassistantartifact_012345abcdef_123456","name":"Auswertung.csv","mime_type":"text/csv","size":1200}}},"application/octet-stream":{"schema":{"type":"string","format":"binary"},"description":"download=1: tatsächlicher Content-Type entspricht mime_type; keine JSON-Verpackung."},"text/csv":{"schema":{"type":"string"}}},"headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}},"Content-Disposition":{"schema":{"type":"string"},"description":"attachment mit URL-kodiertem Dateinamen beim Download."},"Content-Length":{"schema":{"type":"integer"}},"X-Edoobox-Sensitive-Artifact":{"schema":{"type":"string","const":"one-time"},"description":"Nur bei sensiblen Einmal-Downloads."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-sensitive-download":"Einmaliger Benutzerdownload; niemals als Modellinhalt abrufen, in Modelltext einfügen oder automatisch konsumieren.","x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/aiassistant/auth-context Aktuellen Berechtigungs- und Revisionskontext lesen Erfordert authentifizierten Admin- oder API-Kontext und liefert nur die wirksamen Kontoberechtigungen, readOnly, öffentliche Konto-/Admin-ID und eine Mutationsrevision. Admin-Kontext erfordert eine gültige Admin-ID. Der Snapshot wartet auf aktive Schreiber und initialisiert bei Bedarf den Revisionsdatensatz. Berechtigungen sind kein Grant und dürfen weder erweitert noch aus Modelltext übernommen werden. mcpEndpoint ist nur bei vorhandener vertrauenswürdiger Laufzeitbindung enthalten; private no-store-Antwort. - Operation-ID: get_v2_aiassistant_auth_context - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-auth-context-7eb523dd8b22/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/auth-context' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/auth-context"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/auth-context" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_auth_context","summary":"Aktuellen Berechtigungs- und Revisionskontext lesen","description":"Erfordert authentifizierten Admin- oder API-Kontext und liefert nur die wirksamen Kontoberechtigungen, readOnly, öffentliche Konto-/Admin-ID und eine Mutationsrevision. Admin-Kontext erfordert eine gültige Admin-ID. Der Snapshot wartet auf aktive Schreiber und initialisiert bei Bedarf den Revisionsdatensatz. Berechtigungen sind kein Grant und dürfen weder erweitert noch aus Modelltext übernommen werden. mcpEndpoint ist nur bei vorhandener vertrauenswürdiger Laufzeitbindung enthalten; private no-store-Antwort.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Authentifizierter, kontogebundener Berechtigungs-Snapshot mit Revisions- und Uploadpfad-Hashes; keine Roh-DB-IDs oder Anmeldedaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"authenticated":true,"edid":"synthetic-context","mutationRevision":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","readOnly":false,"permissions":{"offe":1},"uploadPathHashes":{"mail":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","file":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"},"authorizationVersion":"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd","account_id":"account_012345abcdef_123456","admin_id":"admin_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"authenticated":{"const":true},"edid":{"type":"string"},"mutationRevision":{"type":"string","pattern":"^[a-f0-9]{32}$"},"readOnly":{"type":"boolean"},"permissions":{"type":"object","additionalProperties":{"type":"integer","minimum":0,"maximum":4}},"uploadPathHashes":{"type":"object","properties":{"mail":{"type":"string"},"file":{"type":"string"}},"additionalProperties":true},"authorizationVersion":{"type":"string"},"account_id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"admin_id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"mcpEndpoint":{"type":"object","properties":{"id":{"type":"string"},"origin":{"type":"string","format":"uri"}},"required":["id","origin"],"additionalProperties":false,"description":"Optionale vertrauenswürdige Endpoint-Laufzeitbindung; keine vom Aufrufer wählbare Zieladresse."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/aiassistant/config KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert Aktivierung, Providerstatus, Tokenbudget, Kaufverfügbarkeit und Berechtigungsanzeigen. Initialisiert bei Bedarf Konto-Einstellungen und Monatsverbrauch und gleicht serverseitig bestätigte Zahlungen ab; dadurch kann bezahltes Token-Guthaben gutgeschrieben oder überprüft werden. Gibt keinen OpenAI-Schlüssel oder sonstige Provider-Zugangsdaten zurück. - Operation-ID: get_v2_aiassistant_config - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-config-435f6efd01b4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/config' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/config"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/config" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_config","summary":"KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert Aktivierung, Providerstatus, Tokenbudget, Kaufverfügbarkeit und Berechtigungsanzeigen. Initialisiert bei Bedarf Konto-Einstellungen und Monatsverbrauch und gleicht serverseitig bestätigte Zahlungen ab; dadurch kann bezahltes Token-Guthaben gutgeschrieben oder überprüft werden. Gibt keinen OpenAI-Schlüssel oder sonstige Provider-Zugangsdaten zurück.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Statusobjekt mit enabled, provider, quota, byok, purchase, retention_days und permissions.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"enabled":true,"provider":{"mode":"platform","configured":true,"simulation":false},"quota":{"month":"2026-10","free_limit":100000,"input_tokens":0,"output_tokens":0,"total_tokens":0,"reserved_tokens":0,"byok_input_tokens":0,"byok_output_tokens":0,"paid_balance_tokens":0,"paid_reserved_tokens":0,"paid_available_tokens":0,"free_remaining_tokens":100000,"remaining_tokens":100000,"payment_review_required":false},"byok":{"configured":false,"scope":"account"},"purchase":{"available":false,"route_name":"system.accounting"},"retention_days":30,"permissions":{"can_configure":true,"can_purchase":false,"can_activate":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"provider":{"type":"object","properties":{"mode":{"enum":["platform","byok"]},"configured":{"type":"boolean"},"simulation":{"type":"boolean"}},"additionalProperties":true},"quota":{"type":"object","properties":{"free_limit":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"total_tokens":{"type":"integer"},"reserved_tokens":{"type":"integer"},"byok_input_tokens":{"type":"integer"},"byok_output_tokens":{"type":"integer"},"paid_balance_tokens":{"type":"integer"},"paid_reserved_tokens":{"type":"integer"},"paid_available_tokens":{"type":"integer"},"free_remaining_tokens":{"type":"integer"},"remaining_tokens":{"type":"integer"},"month":{"type":"string"},"payment_review_required":{"type":"boolean"}},"additionalProperties":true},"byok":{"type":"object","properties":{"configured":{"type":"boolean"},"scope":{"const":"account"}},"additionalProperties":true},"purchase":{"type":"object","properties":{"available":{"type":"boolean"},"route_name":{"const":"system.accounting"}},"additionalProperties":true},"retention_days":{"type":"integer"},"permissions":{"type":"object","properties":{"can_configure":{"type":"boolean"},"can_purchase":{"type":"boolean"},"can_activate":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/aiassistant/config KI-Assistent konfigurieren Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Ändert Provider und optional den kontoweit verschlüsselt gespeicherten OpenAI-Schlüssel. provider=byok erfordert einen wirksamen Schlüssel; openai_key=null entfernt ihn. enabled muss ein JSON-Boolean sein; Aktivierung/Deaktivierung erfordert zusätzlich Plugin-Rechte und request_id. Aktivierung ist nur für das verfügbare kostenlose Assistent-Plugin erlaubt. Einstellungen erfordern setti-Rechte. Schlüssel sind reine Eingabe und erscheinen nie in der Antwort. Alle Änderungsfelder und der JSON-Body sind optional; ein leerer Aufruf aktualisiert nur technische Einstellungen und liest den Status. - Operation-ID: put_v2_aiassistant_config - Dokumentation: /docs/api/v2/operations/put-v2-aiassistant-config-fde2efdb91f3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "provider": "platform" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/config' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/config"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "provider": "platform" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/config" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"provider\":\"platform\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_aiassistant_config","summary":"KI-Assistent konfigurieren","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Ändert Provider und optional den kontoweit verschlüsselt gespeicherten OpenAI-Schlüssel. provider=byok erfordert einen wirksamen Schlüssel; openai_key=null entfernt ihn. enabled muss ein JSON-Boolean sein; Aktivierung/Deaktivierung erfordert zusätzlich Plugin-Rechte und request_id. Aktivierung ist nur für das verfügbare kostenlose Assistent-Plugin erlaubt. Einstellungen erfordern setti-Rechte. Schlüssel sind reine Eingabe und erscheinen nie in der Antwort. Alle Änderungsfelder und der JSON-Body sind optional; ein leerer Aufruf aktualisiert nur technische Einstellungen und liest den Status.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Aktualisierter Status wie GET status; bei configure enthält der Service keine permissions-Ergänzung des GET-Controllers.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"enabled":true,"provider":{"mode":"platform","configured":true,"simulation":false},"quota":{"month":"2026-10","free_limit":100000,"input_tokens":0,"output_tokens":0,"total_tokens":0,"reserved_tokens":0,"byok_input_tokens":0,"byok_output_tokens":0,"paid_balance_tokens":0,"paid_reserved_tokens":0,"paid_available_tokens":0,"free_remaining_tokens":100000,"remaining_tokens":100000,"payment_review_required":false},"byok":{"configured":false,"scope":"account"},"purchase":{"available":false,"route_name":"system.accounting"},"retention_days":30}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"provider":{"type":"object","properties":{"mode":{"enum":["platform","byok"]},"configured":{"type":"boolean"},"simulation":{"type":"boolean"}},"additionalProperties":true},"quota":{"type":"object","properties":{"free_limit":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"total_tokens":{"type":"integer"},"reserved_tokens":{"type":"integer"},"byok_input_tokens":{"type":"integer"},"byok_output_tokens":{"type":"integer"},"paid_balance_tokens":{"type":"integer"},"paid_reserved_tokens":{"type":"integer"},"paid_available_tokens":{"type":"integer"},"free_remaining_tokens":{"type":"integer"},"remaining_tokens":{"type":"integer"},"month":{"type":"string"},"payment_review_required":{"type":"boolean"}},"additionalProperties":true},"byok":{"type":"object","properties":{"configured":{"type":"boolean"},"scope":{"const":"account"}},"additionalProperties":true},"purchase":{"type":"object","properties":{"available":{"type":"boolean"},"route_name":{"const":"system.accounting"}},"additionalProperties":true},"retention_days":{"type":"integer"},"permissions":{"type":"object","properties":{"can_configure":{"type":"boolean"},"can_purchase":{"type":"boolean"},"can_activate":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"provider":{"enum":["platform","byok"]},"openai_key":{"type":["string","null"],"pattern":"^sk-[A-Za-z0-9_-]{16,500}$","writeOnly":true,"description":"Kontoweiter BYOK-Schlüssel; null entfernt ihn. Niemals Antworten, Logs oder Beispielen mit echten Schlüsseln hinzufügen."},"enabled":{"type":"boolean"},"request_id":{"type":["string","number"],"pattern":"^[A-Za-z0-9_.:-]{8,160}$","description":"Nach String-Konvertierung 8–160 Zeichen aus A–Z, a–z, 0–9, _, ., :, -. Pro logisch neuem Auftrag einen neuen Schlüssel wählen; nur identische Wiederholung darf denselben Schlüssel verwenden."}},"additionalProperties":true,"allOf":[{"if":{"required":["enabled"]},"then":{"required":["request_id"]}}]}},"additionalProperties":true},"examples":{"request":{"summary":"KI-Assistent konfigurieren","value":{"data":{"provider":"platform"}}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"updateAiassistant","line":18},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/aiassistant/conversations Eigene KI-Gespräche auflisten Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert höchstens 100 nicht abgelaufene Gespräche, neueste zuerst. Gespräche anderer Administratoren oder abweichender Remote-Endpoint-Bindung werden nicht gezeigt. Generische Paginierung wird hier nicht ausgewertet. - Operation-ID: get_v2_aiassistant_conversations - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-conversations-ff2b60e59eaf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/conversations' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/conversations"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/conversations" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_conversations","summary":"Eigene KI-Gespräche auflisten","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert höchstens 100 nicht abgelaufene Gespräche, neueste zuerst. Gespräche anderer Administratoren oder abweichender Remote-Endpoint-Bindung werden nicht gezeigt. Generische Paginierung wird hier nicht ausgewertet.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Direkte Liste eigener Gespräche mit öffentlichen IDs und Ablaufzeit.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"aiassistantconversation_012345abcdef_123456","chat_id":"aiassistantconversation_012345abcdef_123456","last_response_id":null,"title":"Kursplanung","created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00","expires_at":"2026-11-21T10:00:00+00:00"}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"chat_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"last_response_id":{"anyOf":[{"type":"string","pattern":"^aiassistantmessage_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"null"}]},"title":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id","chat_id","title","created_at","updated_at","expires_at"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/aiassistant/conversations KI-Gespräch beginnen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Legt ein leeres Gespräch mit 30 Tagen Aufbewahrung an. title ist optional, wird getrimmt und auf höchstens 160 Zeichen validiert. Es wird noch keine Modellanfrage ausgeführt. - Operation-ID: post_v2_aiassistant_conversations - Dokumentation: /docs/api/v2/operations/post-v2-aiassistant-conversations-1af1d4d92f35/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "title": "Kursplanung" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/conversations' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/conversations"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "title": "Kursplanung" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/conversations" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"title\":\"Kursplanung\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_aiassistant_conversations","summary":"KI-Gespräch beginnen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Legt ein leeres Gespräch mit 30 Tagen Aufbewahrung an. title ist optional, wird getrimmt und auf höchstens 160 Zeichen validiert. Es wird noch keine Modellanfrage ausgeführt.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Neu angelegtes Gespräch; last_response_id ist zunächst null.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"aiassistantconversation_012345abcdef_123456","chat_id":"aiassistantconversation_012345abcdef_123456","last_response_id":null,"title":"Kursplanung","created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00","expires_at":"2026-11-21T10:00:00+00:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"chat_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"last_response_id":{"anyOf":[{"type":"string","pattern":"^aiassistantmessage_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"null"}]},"title":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id","chat_id","title","created_at","updated_at","expires_at"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"title":{"type":["string","number","boolean","null"],"description":"Wird nach String-Konvertierung getrimmt; höchstens 160 Zeichen."}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"KI-Gespräch beginnen","value":{"data":{"title":"Kursplanung"}}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"postAiassistant","line":14},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/aiassistant/conversations/{conversationId} Eigenes KI-Gespräch löschen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Lässt das Gespräch und seine Läufe sofort ablaufen, fordert laufende Verarbeitung zum Abbruch auf und startet die Bereinigung abgelaufener Assistentdaten. Kein JSON-Body erforderlich. - Operation-ID: delete_v2_aiassistant_conversations_conversationId - Dokumentation: /docs/api/v2/operations/delete-v2-aiassistant-conversations-conversationid-619afa29f057/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | conversationId | path | ja | Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"conversationId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID.","schema":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONVERSATIONID:?PATH_CONVERSATIONID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/conversations/'"${PATH_CONVERSATIONID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/conversations/{conversationId}"; path = path.replaceAll("{conversationId}", encodeURIComponent(required("PATH_CONVERSATIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/conversations/{conversationId}" path = path.replace("{conversationId}", urllib.parse.quote(os.environ["PATH_CONVERSATIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_aiassistant_conversations_conversationId","summary":"Eigenes KI-Gespräch löschen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Lässt das Gespräch und seine Läufe sofort ablaufen, fordert laufende Verarbeitung zum Abbruch auf und startet die Bereinigung abgelaufener Assistentdaten. Kein JSON-Body erforderlich.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"conversationId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID.","schema":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"deleted=true bestätigt das Ablaufenlassen und den Bereinigungsaufruf.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"deleted":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"deleted":{"type":"boolean"}},"additionalProperties":true,"required":["deleted"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Eigenes KI-Gespräch löschen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"deleteAiassistant","line":22},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/aiassistant/conversations/{conversationId} KI-Gespräch mit Nachrichten und Läufen lesen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liest das eigene Gespräch mit bis zu 500 Nachrichten, allen Läufen und dem zuletzt gespeicherten Kontext. Quellenberechtigungen aller gespeicherten Läufe werden erneut geprüft; kein Modellaufruf. - Operation-ID: get_v2_aiassistant_conversations_conversationId - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-conversations-conversationid-a662604c5c8a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | conversationId | path | ja | Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"conversationId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID.","schema":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONVERSATIONID:?PATH_CONVERSATIONID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/conversations/'"${PATH_CONVERSATIONID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/conversations/{conversationId}"; path = path.replaceAll("{conversationId}", encodeURIComponent(required("PATH_CONVERSATIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/conversations/{conversationId}" path = path.replace("{conversationId}", urllib.parse.quote(os.environ["PATH_CONVERSATIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_conversations_conversationId","summary":"KI-Gespräch mit Nachrichten und Läufen lesen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liest das eigene Gespräch mit bis zu 500 Nachrichten, allen Läufen und dem zuletzt gespeicherten Kontext. Quellenberechtigungen aller gespeicherten Läufe werden erneut geprüft; kein Modellaufruf.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"conversationId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID.","schema":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Gespräch, Nachrichtenliste und Laufsliste mit öffentlichen IDs.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"conversation":{"id":"aiassistantconversation_012345abcdef_123456","chat_id":"aiassistantconversation_012345abcdef_123456","last_response_id":null,"title":"Kursplanung","created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00","expires_at":"2026-11-21T10:00:00+00:00","context":{}},"messages":[{"id":"aiassistantmessage_012345abcdef_123456","role":"user","content":"Zeige die Kurse im November.","blocks":[],"created_at":"2026-10-22T10:00:00+00:00"}],"runs":[{"id":"aiassistantrun_012345abcdef_123456","conversation_id":"aiassistantconversation_012345abcdef_123456","status":"queued","phase":"draft","proposal":null,"approved_at":null,"error_code":null,"simulation":false,"usage":{"input_tokens":0,"output_tokens":0},"usage_status":"measured","unresolved_reserved_tokens":0,"estimated_usage":null,"activity":[],"created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00"}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"conversation":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"chat_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"last_response_id":{"anyOf":[{"type":"string","pattern":"^aiassistantmessage_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"null"}]},"title":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"context":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true,"required":["id","chat_id","title","created_at","updated_at","expires_at"]},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantmessage_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"role":{"type":"string"},"content":{"type":"string"},"blocks":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Typabhängige Anzeige-, Referenz- und Anhangsblöcke."}},"created_at":{"type":"string","format":"date-time"},"context":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}},"runs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"conversation_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"status":{"type":"string","enum":["queued","running","succeeded","awaiting_approval","failed","cancelled","authorization_required"]},"phase":{"type":"string"},"proposal":{"anyOf":[{"type":"object","properties":{"summary":{"type":"string"},"hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"actions":{"type":"array","items":{"type":"object","properties":{"tool":{"enum":["editor.replace","edoobox_execute","edoobox_upload_transfer"]},"arguments":{"type":"object","properties":{},"additionalProperties":true,"description":"Exakte serverseitig normalisierte, aktionsabhängige Argumente. Keine vom Client erfundenen Freigabe- oder Zugangsdaten."},"description":{"type":"string"}},"additionalProperties":true,"required":["tool","arguments","description"]},"maxItems":20}},"additionalProperties":true,"required":["summary","actions","hash"]},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time"},"error_code":{"type":["string","null"]},"simulation":{"type":"boolean"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"additionalProperties":true},"usage_status":{"type":"string"},"unresolved_reserved_tokens":{"type":"integer"},"estimated_usage":{"type":["object","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"retry_after_seconds":{"type":"integer"},"activity":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"sequence":{"type":"integer"},"at":{"type":"integer"}},"additionalProperties":true}},"editor_context":{"type":"object","properties":{"language":{"const":"css"},"resource_id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"content_sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"execution":{"type":"object","properties":{"total_actions":{"type":"integer"},"completed_actions":{"type":"integer"},"started_actions":{"type":"integer"},"partial":{"type":"boolean"},"uncertain":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"sequence":{"type":"integer"},"status":{"type":"string"},"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"mutationResult":{"type":"object","properties":{"insert":{"type":"boolean"},"update":{"type":"boolean"},"delete":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true,"required":["id","conversation_id","status","phase","proposal","usage","created_at","updated_at"]}}},"additionalProperties":true,"required":["conversation","messages","runs"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/aiassistant/conversations/{conversationId}/messages KI-Nachricht senden und Entwurf zur Prüfung anfordern Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Speichert die Benutzernachricht, reserviert das Tokenbudget und stellt einen asynchronen Entwurfslauf in die Queue. content muss nach Trimmen nicht leer und höchstens 60000 Bytes sein; request_id ist Pflicht. Gleiche ID mit abweichendem Gespräch/Inhalt/Kontext ergibt idempotency_conflict. Es darf nur ein aktiver Lauf je Gespräch existieren. Kontext und Anhänge gewähren keine zusätzlichen Rechte. Änderungen an edoobox werden zuerst als exakter Vorschlag bereitgestellt und erst nach ausdrücklicher Freigabe per approve ausgeführt. Anhänge: höchstens vier, einzeln 2000000 Bytes, zusammen 5 MiB; identische Bytes doppelt sind unzulässig. Nicht erneut mitgesendete Anhänge eines zuvor erfolgreichen Laufs können übernommen werden. - Operation-ID: post_v2_aiassistant_conversations_conversationId_messages - Dokumentation: /docs/api/v2/operations/post-v2-aiassistant-conversations-conversationid-messages-f7cd33582dcf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | conversationId | path | ja | Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"conversationId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID.","schema":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_CONVERSATIONID:?PATH_CONVERSATIONID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "content": "Zeige die Kurse im November.", "request_id": "review-message-0001", "context": { "route": { "name": "offers", "path": "/offers" }, "entities": [] } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/conversations/'"${PATH_CONVERSATIONID}"'/messages' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/conversations/{conversationId}/messages"; path = path.replaceAll("{conversationId}", encodeURIComponent(required("PATH_CONVERSATIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "content": "Zeige die Kurse im November.", "request_id": "review-message-0001", "context": { "route": { "name": "offers", "path": "/offers" }, "entities": [] } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/conversations/{conversationId}/messages" path = path.replace("{conversationId}", urllib.parse.quote(os.environ["PATH_CONVERSATIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"content\":\"Zeige die Kurse im November.\",\"request_id\":\"review-message-0001\",\"context\":{\"route\":{\"name\":\"offers\",\"path\":\"/offers\"},\"entities\":[]}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_aiassistant_conversations_conversationId_messages","summary":"KI-Nachricht senden und Entwurf zur Prüfung anfordern","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Speichert die Benutzernachricht, reserviert das Tokenbudget und stellt einen asynchronen Entwurfslauf in die Queue. content muss nach Trimmen nicht leer und höchstens 60000 Bytes sein; request_id ist Pflicht. Gleiche ID mit abweichendem Gespräch/Inhalt/Kontext ergibt idempotency_conflict. Es darf nur ein aktiver Lauf je Gespräch existieren. Kontext und Anhänge gewähren keine zusätzlichen Rechte. Änderungen an edoobox werden zuerst als exakter Vorschlag bereitgestellt und erst nach ausdrücklicher Freigabe per approve ausgeführt. Anhänge: höchstens vier, einzeln 2000000 Bytes, zusammen 5 MiB; identische Bytes doppelt sind unzulässig. Nicht erneut mitgesendete Anhänge eines zuvor erfolgreichen Laufs können übernommen werden.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"conversationId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantconversation-ID.","schema":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Neu gespeicherte Nachricht und Lauf; bei identischer Wiederholung ist message=null und run der vorhandene Lauf.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"message":{"id":"aiassistantmessage_012345abcdef_123456","role":"user","content":"Zeige die Kurse im November.","blocks":[],"created_at":"2026-10-22T10:00:00+00:00"},"run":{"id":"aiassistantrun_012345abcdef_123456","conversation_id":"aiassistantconversation_012345abcdef_123456","status":"queued","phase":"draft","proposal":null,"approved_at":null,"error_code":null,"simulation":false,"usage":{"input_tokens":0,"output_tokens":0},"usage_status":"measured","unresolved_reserved_tokens":0,"estimated_usage":null,"activity":[],"created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"message":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantmessage_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"role":{"type":"string"},"content":{"type":"string"},"blocks":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Typabhängige Anzeige-, Referenz- und Anhangsblöcke."}},"created_at":{"type":"string","format":"date-time"},"context":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},{"type":"null"}]},"run":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"conversation_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"status":{"type":"string","enum":["queued","running","succeeded","awaiting_approval","failed","cancelled","authorization_required"]},"phase":{"type":"string"},"proposal":{"anyOf":[{"type":"object","properties":{"summary":{"type":"string"},"hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"actions":{"type":"array","items":{"type":"object","properties":{"tool":{"enum":["editor.replace","edoobox_execute","edoobox_upload_transfer"]},"arguments":{"type":"object","properties":{},"additionalProperties":true,"description":"Exakte serverseitig normalisierte, aktionsabhängige Argumente. Keine vom Client erfundenen Freigabe- oder Zugangsdaten."},"description":{"type":"string"}},"additionalProperties":true,"required":["tool","arguments","description"]},"maxItems":20}},"additionalProperties":true,"required":["summary","actions","hash"]},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time"},"error_code":{"type":["string","null"]},"simulation":{"type":"boolean"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"additionalProperties":true},"usage_status":{"type":"string"},"unresolved_reserved_tokens":{"type":"integer"},"estimated_usage":{"type":["object","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"retry_after_seconds":{"type":"integer"},"activity":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"sequence":{"type":"integer"},"at":{"type":"integer"}},"additionalProperties":true}},"editor_context":{"type":"object","properties":{"language":{"const":"css"},"resource_id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"content_sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"execution":{"type":"object","properties":{"total_actions":{"type":"integer"},"completed_actions":{"type":"integer"},"started_actions":{"type":"integer"},"partial":{"type":"boolean"},"uncertain":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"sequence":{"type":"integer"},"status":{"type":"string"},"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"mutationResult":{"type":"object","properties":{"insert":{"type":"boolean"},"update":{"type":"boolean"},"delete":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true,"required":["id","conversation_id","status","phase","proposal","usage","created_at","updated_at"]}},"additionalProperties":true,"required":["message","run"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"content":{"type":["string","number","boolean","null"],"description":"Nach String-Konvertierung/Trimming nicht leer; höchstens 60000 Bytes."},"request_id":{"type":["string","number"],"pattern":"^[A-Za-z0-9_.:-]{8,160}$","description":"Nach String-Konvertierung 8–160 Zeichen aus A–Z, a–z, 0–9, _, ., :, -. Pro logisch neuem Auftrag einen neuen Schlüssel wählen; nur identische Wiederholung darf denselben Schlüssel verwenden."},"context":{"type":"object","properties":{"route":{"type":"object","properties":{"name":{"type":"string","maxLength":1000},"path":{"type":"string","maxLength":1000},"filters":{"type":"array","items":{"type":"object","properties":{"surface":{"enum":["offers","users","bookings","waitlist"]},"values":{"type":"object","properties":{},"additionalProperties":true,"description":"Bekannte boolesche Filter; status als Integer oder eindeutige Integerliste (0–999, höchstens 10), category nur für offers als öffentliche category_-ID, date_start/date_end als gültiges ISO-Datum/-Zeit."}},"additionalProperties":true,"required":["surface","values"]},"maxItems":4},"selection":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"maxItems":20,"uniqueItems":true},"total_selected":{"type":"integer","maximum":100000}},"additionalProperties":true,"required":["ids","total_selected"],"description":"Jede ausgewählte ID muss zusätzlich in entities enthalten sein; total_selected mindestens Anzahl ids."}},"additionalProperties":true},"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"name":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["id"]}},"editor":{"type":"object","properties":{},"additionalProperties":true,"description":"Optionaler unveröffentlichter Editor-Arbeitsstand. Bei language=css sind content (höchstens 20000 Bytes) und kontoeigene widget_-resource_id Pflicht; content_sha256 wird validiert/ergänzt. Kontext verleiht keine Speicher- oder Quellenberechtigung."}},"additionalProperties":true,"description":"Höchstens 200000 JSON-Bytes. Nur route, entities und editor werden übernommen und sicherheitsgefiltert; unbekannte Kontextfelder werden verworfen."},"attachments":{"type":"array","items":{"type":"object","properties":{"filename":{"type":"string","minLength":1,"maxLength":160,"description":" Dateiname nur aus Unicode-Buchstaben/-Ziffern, _, Punkt, Leerzeichen und Bindestrich; weder . noch .. allein."},"mimeType":{"type":"string","maxLength":120,"pattern":"^[A-Za-z0-9.+-]+/[A-Za-z0-9.+-]+$"},"dataBase64":{"type":"string","contentEncoding":"base64","description":"Kanonisches, nicht leeres Base64; decodiert höchstens 2000000 Bytes je Datei."}},"additionalProperties":false,"required":["filename","mimeType","dataBase64"]},"maxItems":4}},"additionalProperties":true,"required":["content","request_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"KI-Nachricht senden und Entwurf zur Prüfung anfordern","value":{"data":{"content":"Zeige die Kurse im November.","request_id":"review-message-0001","context":{"route":{"name":"offers","path":"/offers"},"entities":[]}}}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"postAiassistant","line":14},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/aiassistant/packages Verfügbare KI-Tokenpakete lesen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert ausschließlich serverseitig konfigurierte Pakete in der Kontowährung. Bei deaktiviertem Tokenkauf oder ungültigem Katalog ist packages leer und purchase_available=false. Preise allein aktivieren keinen Kauf. - Operation-ID: get_v2_aiassistant_packages - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-packages-992ab2c37937/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/packages' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/packages"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/packages" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_packages","summary":"Verfügbare KI-Tokenpakete lesen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert ausschließlich serverseitig konfigurierte Pakete in der Kontowährung. Bei deaktiviertem Tokenkauf oder ungültigem Katalog ist packages leer und purchase_available=false. Preise allein aktivieren keinen Kauf.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Katalog mit Paketcode, Tokenmenge, Dezimalpreis als String und Währung; Verfügbarkeit ausdrücklich separat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"packages":[{"key":"example_package","tokens":100000,"price_amount":"10.00","currency_iso":"CHF"}],"purchase_available":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"packages":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"tokens":{"type":"integer"},"price_amount":{"type":"string"},"currency_iso":{"type":"string","pattern":"^[A-Z]{3}$"}},"additionalProperties":true,"required":["key","tokens","price_amount","currency_iso"]}},"purchase_available":{"type":"boolean"}},"additionalProperties":true,"required":["packages","purchase_available"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":60},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"aiassistant","checkedByDispatcher":false,"description":"Authenticated administrator required by Aiassistant::handle; billing routes also check the listed existing resource rights."}} ``` --- # POST /v2/aiassistant/purchases KI-Tokenpaket gegen Rechnung bestellen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Erstellt eine Kontorechnung und einen ausstehenden Tokenkauf aus dem aktuellen serverseitigen Paketkatalog. package_key und request_id sind Pflicht und echte Strings. Preis, Währung, Tokenzahl und Zahlungsstatus kommen ausschließlich vom Server; clientseitige Angaben dazu werden ignoriert. Identische request_id für denselben Admin und dasselbe Paket ist idempotent; andere Wiederverwendung wird abgelehnt. Erfordert aktivierte Kauf-Funktion sowie setti- und edsu-Rechte. Guthaben entsteht erst nach bestätigtem Zahlungsabgleich. - Operation-ID: post_v2_aiassistant_purchases - Dokumentation: /docs/api/v2/operations/post-v2-aiassistant-purchases-24e2a1bc4480/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "package_key": "example_package", "request_id": "review-purchase-0001" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/purchases' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/purchases"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "package_key": "example_package", "request_id": "review-purchase-0001" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/purchases" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"package_key\":\"example_package\",\"request_id\":\"review-purchase-0001\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_aiassistant_purchases","summary":"KI-Tokenpaket gegen Rechnung bestellen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Erstellt eine Kontorechnung und einen ausstehenden Tokenkauf aus dem aktuellen serverseitigen Paketkatalog. package_key und request_id sind Pflicht und echte Strings. Preis, Währung, Tokenzahl und Zahlungsstatus kommen ausschließlich vom Server; clientseitige Angaben dazu werden ignoriert. Identische request_id für denselben Admin und dasselbe Paket ist idempotent; andere Wiederverwendung wird abgelehnt. Erfordert aktivierte Kauf-Funktion sowie setti- und edsu-Rechte. Guthaben entsteht erst nach bestätigtem Zahlungsabgleich.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Kaufobjekt mit öffentlicher Kauf-/Rechnungs-ID und serverseitigem Zahlungsbezug; pending ist noch keine Gutschrift.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"aiassistantpurchase_012345abcdef_123456","package_key":"example_package","tokens":100000,"price_amount":"10.00","currency_iso":"CHF","status":"pending","created_at":1792663200,"credited_at":0,"invoice":{"id":"edinvoice_012345abcdef_123456","url":"/v2/edsubscription/synthetic-reference/pay/invoice.pdf"},"payment":{"type":"invoice","token":"synthetic-reference","invoice_id":"edinvoice_012345abcdef_123456","route_name":"system.accounting"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantpurchase_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"package_key":{"type":"string"},"tokens":{"type":"integer"},"price_amount":{"type":["string","number"]},"currency_iso":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"integer"},"credited_at":{"type":"integer"},"invoice":{"type":"object","properties":{"id":{"type":"string","pattern":"^edinvoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"url":{"type":"string"}},"additionalProperties":true},"payment":{"type":"object","properties":{"type":{"const":"invoice"},"token":{"type":"string"},"invoice_id":{"type":"string","pattern":"^edinvoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"route_name":{"const":"system.accounting"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"package_key":{"type":"string"},"request_id":{"type":"string","pattern":"^[A-Za-z0-9_.:-]{8,160}$"}},"additionalProperties":true,"required":["package_key","request_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"KI-Tokenpaket gegen Rechnung bestellen","value":{"data":{"package_key":"example_package","request_id":"review-purchase-0001"}}}}}}},"x-edoobox-examplePrerequisite":"Enabled account catalog must contain the exact key. Values mirror tests/V2/AiAssistantTokenPurchaseTest.php; client amounts/paid flags are not trusted.","x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"postAiassistant","line":64},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","checkedByDispatcher":false,"description":"Authenticated administrator required by Aiassistant::handle; billing routes also check the listed existing resource rights.","requiredRights":{"setti":[3,4],"edsu":[2,3,4]}}} ``` --- # GET /v2/aiassistant/purchases/{id} Tokenkauf und Zahlungsabgleich abrufen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liest einen eigenen Kauf über seine öffentliche aiassistantpurchase_-ID. Der Abruf gleicht serverseitig bestätigte Kontorechnungen mit Tokenkäufen ab und kann Guthaben gutschreiben bzw. einen Prüfstatus setzen. Browserparameter können keine Zahlung bestätigen. Erfordert Leseberechtigung für Einstellungen und Kontorechnung. - Operation-ID: get_v2_aiassistant_purchases_id - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-purchases-id-9cb9c61f8f81/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Kauf-ID. | | language | header | nein | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^aiassistantpurchase_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["aiassistantpurchase_012345abcdef_123456"]},"description":"Öffentliche Kauf-ID."},{"name":"language","in":"header","required":false,"schema":{"type":"string"}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/purchases/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/purchases/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/purchases/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_purchases_id","summary":"Tokenkauf und Zahlungsabgleich abrufen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liest einen eigenen Kauf über seine öffentliche aiassistantpurchase_-ID. Der Abruf gleicht serverseitig bestätigte Kontorechnungen mit Tokenkäufen ab und kann Guthaben gutschreiben bzw. einen Prüfstatus setzen. Browserparameter können keine Zahlung bestätigen. Erfordert Leseberechtigung für Einstellungen und Kontorechnung.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^aiassistantpurchase_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["aiassistantpurchase_012345abcdef_123456"]},"description":"Öffentliche Kauf-ID."},{"name":"language","in":"header","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Aktueller Kauf nach serverseitigem Zahlungsabgleich, inklusive Rechnungs- und Zahlungsbezug.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"aiassistantpurchase_012345abcdef_123456","package_key":"example_package","tokens":100000,"price_amount":"10.00","currency_iso":"CHF","status":"pending","created_at":1792663200,"credited_at":0,"invoice":{"id":"edinvoice_012345abcdef_123456","url":"/v2/edsubscription/synthetic-reference/pay/invoice.pdf"},"payment":{"type":"invoice","token":"synthetic-reference","invoice_id":"edinvoice_012345abcdef_123456","route_name":"system.accounting"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantpurchase_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"package_key":{"type":"string"},"tokens":{"type":"integer"},"price_amount":{"type":["string","number"]},"currency_iso":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"integer"},"credited_at":{"type":"integer"},"invoice":{"type":"object","properties":{"id":{"type":"string","pattern":"^edinvoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"url":{"type":"string"}},"additionalProperties":true},"payment":{"type":"object","properties":{"type":{"const":"invoice"},"token":{"type":"string"},"invoice_id":{"type":"string","pattern":"^edinvoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"route_name":{"const":"system.accounting"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":64},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","checkedByDispatcher":false,"description":"Authenticated administrator required by Aiassistant::handle; billing routes also check the listed existing resource rights.","requiredRights":{"setti":[1,3,4],"edsu":[1,3,4]}}} ``` --- # GET /v2/aiassistant/runs/{runId} KI-Laufstatus und geprüften Änderungsvorschlag lesen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liest Status, öffentliche Lauf-/Gesprächs-ID, Verbrauch, sichere Fortschrittscodes und gegebenenfalls einen konkreten Vorschlag samt Hash. Der interne Status approved wird öffentlich als queued ausgegeben. Nach Freigabe kann execution Einzelergebnisse, partial und uncertain enthalten; ein erfolgreich empfangener HTTP-Request beweist keine vollständige Mutation. Quellenberechtigungen werden erneut geprüft. - Operation-ID: get_v2_aiassistant_runs_runId - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-runs-runid-a91d8fa49830/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | runId | path | ja | Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"runId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID.","schema":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_RUNID:?PATH_RUNID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/runs/'"${PATH_RUNID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/runs/{runId}"; path = path.replaceAll("{runId}", encodeURIComponent(required("PATH_RUNID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/runs/{runId}" path = path.replace("{runId}", urllib.parse.quote(os.environ["PATH_RUNID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_runs_runId","summary":"KI-Laufstatus und geprüften Änderungsvorschlag lesen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liest Status, öffentliche Lauf-/Gesprächs-ID, Verbrauch, sichere Fortschrittscodes und gegebenenfalls einen konkreten Vorschlag samt Hash. Der interne Status approved wird öffentlich als queued ausgegeben. Nach Freigabe kann execution Einzelergebnisse, partial und uncertain enthalten; ein erfolgreich empfangener HTTP-Request beweist keine vollständige Mutation. Quellenberechtigungen werden erneut geprüft.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"runId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID.","schema":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Laufobjekt; proposal ist null oder der zur expliziten Prüfung anstehende Vorschlag mit Hash.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"aiassistantrun_012345abcdef_123456","conversation_id":"aiassistantconversation_012345abcdef_123456","status":"queued","phase":"draft","proposal":null,"approved_at":null,"error_code":null,"simulation":false,"usage":{"input_tokens":0,"output_tokens":0},"usage_status":"measured","unresolved_reserved_tokens":0,"estimated_usage":null,"activity":[],"created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"conversation_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"status":{"type":"string","enum":["queued","running","succeeded","awaiting_approval","failed","cancelled","authorization_required"]},"phase":{"type":"string"},"proposal":{"anyOf":[{"type":"object","properties":{"summary":{"type":"string"},"hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"actions":{"type":"array","items":{"type":"object","properties":{"tool":{"enum":["editor.replace","edoobox_execute","edoobox_upload_transfer"]},"arguments":{"type":"object","properties":{},"additionalProperties":true,"description":"Exakte serverseitig normalisierte, aktionsabhängige Argumente. Keine vom Client erfundenen Freigabe- oder Zugangsdaten."},"description":{"type":"string"}},"additionalProperties":true,"required":["tool","arguments","description"]},"maxItems":20}},"additionalProperties":true,"required":["summary","actions","hash"]},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time"},"error_code":{"type":["string","null"]},"simulation":{"type":"boolean"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"additionalProperties":true},"usage_status":{"type":"string"},"unresolved_reserved_tokens":{"type":"integer"},"estimated_usage":{"type":["object","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"retry_after_seconds":{"type":"integer"},"activity":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"sequence":{"type":"integer"},"at":{"type":"integer"}},"additionalProperties":true}},"editor_context":{"type":"object","properties":{"language":{"const":"css"},"resource_id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"content_sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"execution":{"type":"object","properties":{"total_actions":{"type":"integer"},"completed_actions":{"type":"integer"},"started_actions":{"type":"integer"},"partial":{"type":"boolean"},"uncertain":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"sequence":{"type":"integer"},"status":{"type":"string"},"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"mutationResult":{"type":"object","properties":{"insert":{"type":"boolean"},"update":{"type":"boolean"},"delete":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true,"required":["id","conversation_id","status","phase","proposal","usage","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/aiassistant/runs/{runId}/approve Geprüften KI-Änderungsvorschlag ausdrücklich freigeben Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Gibt ausschließlich den gespeicherten Vorschlag mit exakt passendem proposal_hash frei. request_id und proposal_hash sind Pflicht. Nur awaiting_approval und nicht abgebrochene Läufe sind neu freigebbar; geänderter Hash ergibt proposal_changed. Identische bereits freigegebene Hashes liefern den bisherigen Lauf. Editor-only-Vorschläge werden sofort als succeeded markiert; API-/Upload-Aktionen werden zur kontrollierten Ausführung eingereiht. Freigabe muss nach menschlicher Prüfung aller konkreten Aktionen erfolgen; Modelltext, Kontext oder clientseitige approved-Flags sind keine Autorisierung. Quellen- und aktuelle Kontoberechtigungen bleiben verbindlich. - Operation-ID: post_v2_aiassistant_runs_runId_approve - Dokumentation: /docs/api/v2/operations/post-v2-aiassistant-runs-runid-approve-b0ac3dd7b395/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | runId | path | ja | Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"runId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID.","schema":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_RUNID:?PATH_RUNID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "request_id": "review-approval-0001", "proposal_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/runs/'"${PATH_RUNID}"'/approve' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/runs/{runId}/approve"; path = path.replaceAll("{runId}", encodeURIComponent(required("PATH_RUNID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "request_id": "review-approval-0001", "proposal_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/runs/{runId}/approve" path = path.replace("{runId}", urllib.parse.quote(os.environ["PATH_RUNID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"request_id\":\"review-approval-0001\",\"proposal_hash\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_aiassistant_runs_runId_approve","summary":"Geprüften KI-Änderungsvorschlag ausdrücklich freigeben","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Gibt ausschließlich den gespeicherten Vorschlag mit exakt passendem proposal_hash frei. request_id und proposal_hash sind Pflicht. Nur awaiting_approval und nicht abgebrochene Läufe sind neu freigebbar; geänderter Hash ergibt proposal_changed. Identische bereits freigegebene Hashes liefern den bisherigen Lauf. Editor-only-Vorschläge werden sofort als succeeded markiert; API-/Upload-Aktionen werden zur kontrollierten Ausführung eingereiht. Freigabe muss nach menschlicher Prüfung aller konkreten Aktionen erfolgen; Modelltext, Kontext oder clientseitige approved-Flags sind keine Autorisierung. Quellen- und aktuelle Kontoberechtigungen bleiben verbindlich.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"runId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID.","schema":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Aktualisierter Lauf; queued bedeutet Freigabe angenommen, nicht vollständig ausgeführt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"aiassistantrun_012345abcdef_123456","conversation_id":"aiassistantconversation_012345abcdef_123456","status":"queued","phase":"approved","proposal":null,"approved_at":null,"error_code":null,"simulation":false,"usage":{"input_tokens":0,"output_tokens":0},"usage_status":"measured","unresolved_reserved_tokens":0,"estimated_usage":null,"activity":[],"created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"conversation_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"status":{"type":"string","enum":["queued","running","succeeded","awaiting_approval","failed","cancelled","authorization_required"]},"phase":{"type":"string"},"proposal":{"anyOf":[{"type":"object","properties":{"summary":{"type":"string"},"hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"actions":{"type":"array","items":{"type":"object","properties":{"tool":{"enum":["editor.replace","edoobox_execute","edoobox_upload_transfer"]},"arguments":{"type":"object","properties":{},"additionalProperties":true,"description":"Exakte serverseitig normalisierte, aktionsabhängige Argumente. Keine vom Client erfundenen Freigabe- oder Zugangsdaten."},"description":{"type":"string"}},"additionalProperties":true,"required":["tool","arguments","description"]},"maxItems":20}},"additionalProperties":true,"required":["summary","actions","hash"]},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time"},"error_code":{"type":["string","null"]},"simulation":{"type":"boolean"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"additionalProperties":true},"usage_status":{"type":"string"},"unresolved_reserved_tokens":{"type":"integer"},"estimated_usage":{"type":["object","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"retry_after_seconds":{"type":"integer"},"activity":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"sequence":{"type":"integer"},"at":{"type":"integer"}},"additionalProperties":true}},"editor_context":{"type":"object","properties":{"language":{"const":"css"},"resource_id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"content_sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"execution":{"type":"object","properties":{"total_actions":{"type":"integer"},"completed_actions":{"type":"integer"},"started_actions":{"type":"integer"},"partial":{"type":"boolean"},"uncertain":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"sequence":{"type":"integer"},"status":{"type":"string"},"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"mutationResult":{"type":"object","properties":{"insert":{"type":"boolean"},"update":{"type":"boolean"},"delete":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true,"required":["id","conversation_id","status","phase","proposal","usage","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"request_id":{"type":["string","number"],"pattern":"^[A-Za-z0-9_.:-]{8,160}$","description":"Nach String-Konvertierung 8–160 Zeichen aus A–Z, a–z, 0–9, _, ., :, -. Pro logisch neuem Auftrag einen neuen Schlüssel wählen; nur identische Wiederholung darf denselben Schlüssel verwenden."},"proposal_hash":{"type":["string","number"],"description":"Exakt der zuletzt gelesene proposal.hash; serverseitiger SHA-256-Hash des vollständigen Vorschlags."}},"additionalProperties":true,"required":["request_id","proposal_hash"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Geprüften KI-Änderungsvorschlag ausdrücklich freigeben","value":{"data":{"request_id":"review-approval-0001","proposal_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"postAiassistant","line":14},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/aiassistant/runs/{runId}/cancel KI-Lauf abbrechen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Fordert den Abbruch eines eigenen Laufs an. Ein noch nicht laufender Auftrag wird sofort cancelled und sein Budget ausgeglichen; bei running setzt der Server zunächst die Abbruchanforderung. Bereits abgeschlossene Läufe werden unverändert zurückgegeben. Abbruch macht bereits ausgeführte fachliche Aktionen nicht rückgängig. Kein JSON-Body erforderlich. - Operation-ID: post_v2_aiassistant_runs_runId_cancel - Dokumentation: /docs/api/v2/operations/post-v2-aiassistant-runs-runid-cancel-209adfe84c73/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | runId | path | ja | Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"runId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID.","schema":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_RUNID:?PATH_RUNID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/runs/'"${PATH_RUNID}"'/cancel' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/runs/{runId}/cancel"; path = path.replaceAll("{runId}", encodeURIComponent(required("PATH_RUNID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/runs/{runId}/cancel" path = path.replace("{runId}", urllib.parse.quote(os.environ["PATH_RUNID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_aiassistant_runs_runId_cancel","summary":"KI-Lauf abbrechen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Fordert den Abbruch eines eigenen Laufs an. Ein noch nicht laufender Auftrag wird sofort cancelled und sein Budget ausgeglichen; bei running setzt der Server zunächst die Abbruchanforderung. Bereits abgeschlossene Läufe werden unverändert zurückgegeben. Abbruch macht bereits ausgeführte fachliche Aktionen nicht rückgängig. Kein JSON-Body erforderlich.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"runId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID.","schema":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Aktueller Laufstatus nach Abbruchanforderung; laufende Verarbeitung kann zunächst weiterhin running sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"aiassistantrun_012345abcdef_123456","conversation_id":"aiassistantconversation_012345abcdef_123456","status":"cancelled","phase":"draft","proposal":null,"approved_at":null,"error_code":null,"simulation":false,"usage":{"input_tokens":0,"output_tokens":0},"usage_status":"measured","unresolved_reserved_tokens":0,"estimated_usage":null,"activity":[],"created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"conversation_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"status":{"type":"string","enum":["queued","running","succeeded","awaiting_approval","failed","cancelled","authorization_required"]},"phase":{"type":"string"},"proposal":{"anyOf":[{"type":"object","properties":{"summary":{"type":"string"},"hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"actions":{"type":"array","items":{"type":"object","properties":{"tool":{"enum":["editor.replace","edoobox_execute","edoobox_upload_transfer"]},"arguments":{"type":"object","properties":{},"additionalProperties":true,"description":"Exakte serverseitig normalisierte, aktionsabhängige Argumente. Keine vom Client erfundenen Freigabe- oder Zugangsdaten."},"description":{"type":"string"}},"additionalProperties":true,"required":["tool","arguments","description"]},"maxItems":20}},"additionalProperties":true,"required":["summary","actions","hash"]},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time"},"error_code":{"type":["string","null"]},"simulation":{"type":"boolean"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"additionalProperties":true},"usage_status":{"type":"string"},"unresolved_reserved_tokens":{"type":"integer"},"estimated_usage":{"type":["object","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"retry_after_seconds":{"type":"integer"},"activity":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"sequence":{"type":"integer"},"at":{"type":"integer"}},"additionalProperties":true}},"editor_context":{"type":"object","properties":{"language":{"const":"css"},"resource_id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"content_sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"execution":{"type":"object","properties":{"total_actions":{"type":"integer"},"completed_actions":{"type":"integer"},"started_actions":{"type":"integer"},"partial":{"type":"boolean"},"uncertain":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"sequence":{"type":"integer"},"status":{"type":"string"},"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"mutationResult":{"type":"object","properties":{"insert":{"type":"boolean"},"update":{"type":"boolean"},"delete":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true,"required":["id","conversation_id","status","phase","proposal","usage","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"KI-Lauf abbrechen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"postAiassistant","line":14},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/aiassistant/runs/{runId}/retry Fehlgeschlagenen KI-Entwurf mit neuer Anfrage wiederholen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Stellt einen eigenen failed-, authorization_required- oder cancelled-Entwurf erneut ein, mit gespeicherter Nachricht, Kontext, Anhängen und delegierten Rechten. request_id ist Pflicht. Bereits freigegebene Mutation oder vorhandene Aktionsausführung ist nicht wiederholbar und verlangt einen neuen Entwurf (mutation_retry_requires_new_draft). Diese Route darf nicht zur automatischen Wiederholung unklar abgeschlossener Änderungen verwendet werden. - Operation-ID: post_v2_aiassistant_runs_runId_retry - Dokumentation: /docs/api/v2/operations/post-v2-aiassistant-runs-runid-retry-215203d2c480/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | runId | path | ja | Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"runId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID.","schema":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_RUNID:?PATH_RUNID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "request_id": "review-retry-0001" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/runs/'"${PATH_RUNID}"'/retry' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/runs/{runId}/retry"; path = path.replaceAll("{runId}", encodeURIComponent(required("PATH_RUNID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "request_id": "review-retry-0001" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/runs/{runId}/retry" path = path.replace("{runId}", urllib.parse.quote(os.environ["PATH_RUNID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"request_id\":\"review-retry-0001\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_aiassistant_runs_runId_retry","summary":"Fehlgeschlagenen KI-Entwurf mit neuer Anfrage wiederholen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Stellt einen eigenen failed-, authorization_required- oder cancelled-Entwurf erneut ein, mit gespeicherter Nachricht, Kontext, Anhängen und delegierten Rechten. request_id ist Pflicht. Bereits freigegebene Mutation oder vorhandene Aktionsausführung ist nicht wiederholbar und verlangt einen neuen Entwurf (mutation_retry_requires_new_draft). Diese Route darf nicht zur automatischen Wiederholung unklar abgeschlossener Änderungen verwendet werden.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"runId","in":"path","required":true,"description":"Öffentliche, an Konto und Administrator gebundene aiassistantrun-ID.","schema":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Neuer bzw. idempotent vorhandener Entwurfslauf; die sichtbare Benutzernachricht wird nicht dupliziert.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"message":null,"run":{"id":"aiassistantrun_012345abcdef_123456","conversation_id":"aiassistantconversation_012345abcdef_123456","status":"queued","phase":"draft","proposal":null,"approved_at":null,"error_code":null,"simulation":false,"usage":{"input_tokens":0,"output_tokens":0},"usage_status":"measured","unresolved_reserved_tokens":0,"estimated_usage":null,"activity":[],"created_at":"2026-10-22T10:00:00+00:00","updated_at":"2026-10-22T10:00:00+00:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"message":{"type":"null"},"run":{"type":"object","properties":{"id":{"type":"string","pattern":"^aiassistantrun_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"conversation_id":{"type":"string","pattern":"^aiassistantconversation_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"status":{"type":"string","enum":["queued","running","succeeded","awaiting_approval","failed","cancelled","authorization_required"]},"phase":{"type":"string"},"proposal":{"anyOf":[{"type":"object","properties":{"summary":{"type":"string"},"hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"actions":{"type":"array","items":{"type":"object","properties":{"tool":{"enum":["editor.replace","edoobox_execute","edoobox_upload_transfer"]},"arguments":{"type":"object","properties":{},"additionalProperties":true,"description":"Exakte serverseitig normalisierte, aktionsabhängige Argumente. Keine vom Client erfundenen Freigabe- oder Zugangsdaten."},"description":{"type":"string"}},"additionalProperties":true,"required":["tool","arguments","description"]},"maxItems":20}},"additionalProperties":true,"required":["summary","actions","hash"]},{"type":"null"}]},"approved_at":{"type":["string","null"],"format":"date-time"},"error_code":{"type":["string","null"]},"simulation":{"type":"boolean"},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"additionalProperties":true},"usage_status":{"type":"string"},"unresolved_reserved_tokens":{"type":"integer"},"estimated_usage":{"type":["object","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"retry_after_seconds":{"type":"integer"},"activity":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"sequence":{"type":"integer"},"at":{"type":"integer"}},"additionalProperties":true}},"editor_context":{"type":"object","properties":{"language":{"const":"css"},"resource_id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"content_sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"execution":{"type":"object","properties":{"total_actions":{"type":"integer"},"completed_actions":{"type":"integer"},"started_actions":{"type":"integer"},"partial":{"type":"boolean"},"uncertain":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"sequence":{"type":"integer"},"status":{"type":"string"},"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"mutationResult":{"type":"object","properties":{"insert":{"type":"boolean"},"update":{"type":"boolean"},"delete":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true,"required":["id","conversation_id","status","phase","proposal","usage","created_at","updated_at"]}},"additionalProperties":true,"required":["message","run"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"request_id":{"type":["string","number"],"pattern":"^[A-Za-z0-9_.:-]{8,160}$","description":"Nach String-Konvertierung 8–160 Zeichen aus A–Z, a–z, 0–9, _, ., :, -. Pro logisch neuem Auftrag einen neuen Schlüssel wählen; nur identische Wiederholung darf denselben Schlüssel verwenden."}},"additionalProperties":true,"required":["request_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Fehlgeschlagenen KI-Entwurf mit neuer Anfrage wiederholen","value":{"data":{"request_id":"review-retry-0001"}}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"postAiassistant","line":14},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/aiassistant/status KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert Aktivierung, Providerstatus, Tokenbudget, Kaufverfügbarkeit und Berechtigungsanzeigen. Initialisiert bei Bedarf Konto-Einstellungen und Monatsverbrauch und gleicht serverseitig bestätigte Zahlungen ab; dadurch kann bezahltes Token-Guthaben gutgeschrieben oder überprüft werden. Gibt keinen OpenAI-Schlüssel oder sonstige Provider-Zugangsdaten zurück. - Operation-ID: get_v2_aiassistant_status - Dokumentation: /docs/api/v2/operations/get-v2-aiassistant-status-46e5778eecd2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/aiassistant/status' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/aiassistant/status"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/aiassistant/status" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_aiassistant_status","summary":"KI-Assistent: Aktivierung, Budget und Fähigkeiten lesen","description":"Erfordert einen authentifizierten Administrator im ausgewählten Konto. Gespräche, Läufe und Artefakte sind zusätzlich an diesen Administrator gebunden; Quellenberechtigungen und Ablaufzeit werden erneut geprüft. Liefert Aktivierung, Providerstatus, Tokenbudget, Kaufverfügbarkeit und Berechtigungsanzeigen. Initialisiert bei Bedarf Konto-Einstellungen und Monatsverbrauch und gleicht serverseitig bestätigte Zahlungen ab; dadurch kann bezahltes Token-Guthaben gutgeschrieben oder überprüft werden. Gibt keinen OpenAI-Schlüssel oder sonstige Provider-Zugangsdaten zurück.","tags":["aiassistant"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Statusobjekt mit enabled, provider, quota, byok, purchase, retention_days und permissions.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"enabled":true,"provider":{"mode":"platform","configured":true,"simulation":false},"quota":{"month":"2026-10","free_limit":100000,"input_tokens":0,"output_tokens":0,"total_tokens":0,"reserved_tokens":0,"byok_input_tokens":0,"byok_output_tokens":0,"paid_balance_tokens":0,"paid_reserved_tokens":0,"paid_available_tokens":0,"free_remaining_tokens":100000,"remaining_tokens":100000,"payment_review_required":false},"byok":{"configured":false,"scope":"account"},"purchase":{"available":false,"route_name":"system.accounting"},"retention_days":30,"permissions":{"can_configure":true,"can_purchase":false,"can_activate":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"provider":{"type":"object","properties":{"mode":{"enum":["platform","byok"]},"configured":{"type":"boolean"},"simulation":{"type":"boolean"}},"additionalProperties":true},"quota":{"type":"object","properties":{"free_limit":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"},"total_tokens":{"type":"integer"},"reserved_tokens":{"type":"integer"},"byok_input_tokens":{"type":"integer"},"byok_output_tokens":{"type":"integer"},"paid_balance_tokens":{"type":"integer"},"paid_reserved_tokens":{"type":"integer"},"paid_available_tokens":{"type":"integer"},"free_remaining_tokens":{"type":"integer"},"remaining_tokens":{"type":"integer"},"month":{"type":"string"},"payment_review_required":{"type":"boolean"}},"additionalProperties":true},"byok":{"type":"object","properties":{"configured":{"type":"boolean"},"scope":{"const":"account"}},"additionalProperties":true},"purchase":{"type":"object","properties":{"available":{"type":"boolean"},"route_name":{"const":"system.accounting"}},"additionalProperties":true},"retention_days":{"type":"integer"},"permissions":{"type":"object","properties":{"can_configure":{"type":"boolean"},"can_purchase":{"type":"boolean"},"can_activate":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Aiassistant.php","line":26,"reason":"Dedicated administrator/account identity, ownership, plugin and action-specific settings/billing checks. No legacy permission.short exists; maintenance side effects remain conservatively classified and must not acquire fictitious legacy write permission."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Aiassistant.php","method":"getAiassistant","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"aiassistant","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/apiaccess API-Zugang entfernen Entfernt den API-Zugang über den nativen Löschvertrag. Bereits ausgestellte Login-Kontexte werden im vorhandenen Hook nicht zusätzlich aktiv gesperrt. - Operation-ID: delete_v2_apiaccess - Dokumentation: /docs/api/v2/operations/delete-v2-apiaccess-3718b625bfcb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["apiaccess_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "api_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/apiaccess' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/apiaccess"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "api_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/apiaccess" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"api_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_apiaccess","summary":"API-Zugang entfernen","description":"Entfernt den API-Zugang über den nativen Löschvertrag. Bereits ausgestellte Login-Kontexte werden im vorhandenen Hook nicht zusätzlich aktiv gesperrt.","tags":["apiaccess"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["apiaccess_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"apiaccess_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"API-Zugang entfernen","value":{"id":"api_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Apiaccess.php","method":"deleteApiaccess","line":134},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"apiaccess","short":"apia","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/apiaccess API-Zugänge auflisten Liest die im gewählten Konto verfügbaren API-Zugänge. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft. - Operation-ID: get_v2_apiaccess - Dokumentation: /docs/api/v2/operations/get-v2-apiaccess-7fe54754648f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/apiaccess' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/apiaccess"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/apiaccess" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_apiaccess","summary":"API-Zugänge auflisten","description":"Liest die im gewählten Konto verfügbaren API-Zugänge. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft.","tags":["apiaccess"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"api_012345abcdef_123456":{"id":"api_012345abcdef_123456","status":false,"permission":"permission_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitiveResponseFields":["api_key"],"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Apiaccess.php","method":"getApiaccess","line":150},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"apiaccess","short":"apia","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/apiaccess API-Zugang anlegen Erzeugt serverseitig ein neues key_/sec_-Paar. Ohne permission bzw. mit den dokumentierten leeren Legacy-Werten wird das konto-eigene Standardprofil verwendet; eine explizite permission muss diesem Konto gehören. Das Klartext-Secret wird nur bei dieser Erstellung ausgegeben. - Operation-ID: post_v2_apiaccess - Dokumentation: /docs/api/v2/operations/post-v2-apiaccess-d47d75f646b6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false, "permission": "permission_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/apiaccess' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/apiaccess"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false, "permission": "permission_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/apiaccess" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false,\"permission\":\"permission_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_apiaccess","summary":"API-Zugang anlegen","description":"Erzeugt serverseitig ein neues key_/sec_-Paar. Ohne permission bzw. mit den dokumentierten leeren Legacy-Werten wird das konto-eigene Standardprofil verwendet; eine explizite permission muss diesem Konto gehören. Das Klartext-Secret wird nur bei dieser Erstellung ausgegeben.","tags":["apiaccess"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nativer Insert-Bericht, ergänzt um das serverseitig erzeugte api_key/api_secret-Paar. Bei Batch-Inserts enthält data zusätzlich die einzelnen Insert-Berichte; Klartext-Secret ist sensibel.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"api_012345abcdef_123456","insert":true,"api_key":"key_SYNTHETIC_NOT_A_CREDENTIAL","api_secret":"sec_SYNTHETIC_NOT_A_CREDENTIAL"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","required":["api_key","api_secret"],"properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$"},"insert":{"type":"boolean"},"api_key":{"type":"string","x-edoobox-sensitive":true},"api_secret":{"type":"string","x-edoobox-sensitive":true,"description":"Nur diese Erstellung gibt das Klartext-Secret aus; sicher ablegen und nicht protokollieren."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"api_key":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":45},"permission":{"anyOf":[{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},{"enum":[null,false,0,"0",""]}],"description":"An existing account-owned public permission ID selects that role. Omitted permission or the explicit legacy values null/false/0/\"0\"/\"\" resolve the account default, which must also exist in this account.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"api_key":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":45},"permission":{"anyOf":[{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},{"enum":[null,false,0,"0",""]}],"description":"An existing account-owned public permission ID selects that role. Omitted permission or the explicit legacy values null/false/0/\"0\"/\"\" resolve the account default, which must also exist in this account.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply."}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["apiaccess_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"API-Zugang anlegen","value":{"data":{"status":false,"permission":"permission_012345abcdef_123456"}}}}}}},"x-edoobox-sensitiveResponseFields":["api_key","api_secret"],"x-edoobox-user-handoff":{"sensitivePaths":["/data/api_key","/data/api_secret"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/api_key","/data/api_secret"],"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Apiaccess.php","method":"postApiaccess","line":79},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"apiaccess","short":"apia","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/apiaccess API-Zugang ändern Ändert status oder das konto-eigene Berechtigungsprofil eines vorhandenen API-Zugangs. api_key bleibt unverändert; ein neues Klartext-Secret wird hierbei nicht erzeugt. Die öffentliche Ziel-ID muss als id im JSON-Request angegeben werden. - Operation-ID: put_v2_apiaccess - Dokumentation: /docs/api/v2/operations/put-v2-apiaccess-121dcf58cca9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "api_012345abcdef_123456", "data": { "status": false } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/apiaccess' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/apiaccess"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "api_012345abcdef_123456", "data": { "status": false } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/apiaccess" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"api_012345abcdef_123456\",\"data\":{\"status\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_apiaccess","summary":"API-Zugang ändern","description":"Ändert status oder das konto-eigene Berechtigungsprofil eines vorhandenen API-Zugangs. api_key bleibt unverändert; ein neues Klartext-Secret wird hierbei nicht erzeugt. Die öffentliche Ziel-ID muss als id im JSON-Request angegeben werden.","tags":["apiaccess"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"apiaccess_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"api_key":false,"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"A typed public permission ID must decode positively and exist in firma_permission for this account. Wrong type, missing or foreign roles throw ED80100 with require.permission=false; no default/clear fallback on PUT.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["api_key"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$"}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"API-Zugang ändern","value":{"id":"api_012345abcdef_123456","data":{"status":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Apiaccess.php","method":"updateApiaccess","line":117},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"apiaccess","short":"apia","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/apiaccess/{id} API-Zugang entfernen Entfernt den API-Zugang über den nativen Löschvertrag. Bereits ausgestellte Login-Kontexte werden im vorhandenen Hook nicht zusätzlich aktiv gesperrt. - Operation-ID: delete_v2_apiaccess_id - Dokumentation: /docs/api/v2/operations/delete-v2-apiaccess-id-75ee806380b3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID.","schema":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["api_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/apiaccess/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/apiaccess/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/apiaccess/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_apiaccess_id","summary":"API-Zugang entfernen","description":"Entfernt den API-Zugang über den nativen Löschvertrag. Bereits ausgestellte Login-Kontexte werden im vorhandenen Hook nicht zusätzlich aktiv gesperrt.","tags":["apiaccess"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID.","schema":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["api_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"apiaccess_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"examples":{"request":{"summary":"API-Zugang entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Apiaccess.php","method":"deleteApiaccess","line":134},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"apiaccess","short":"apia","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/apiaccess/{id} API-Zugang lesen Liest den durch die öffentliche Pfad-ID gewählten API-Zugang. Auch ein Einzelabruf liefert die normale GET-Datenstruktur; Feldauswahl, Filter und Konto-Berechtigungen bleiben wirksam. - Operation-ID: get_v2_apiaccess_id - Dokumentation: /docs/api/v2/operations/get-v2-apiaccess-id-cf301681dce4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID.","schema":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["api_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/apiaccess/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/apiaccess/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/apiaccess/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_apiaccess_id","summary":"API-Zugang lesen","description":"Liest den durch die öffentliche Pfad-ID gewählten API-Zugang. Auch ein Einzelabruf liefert die normale GET-Datenstruktur; Feldauswahl, Filter und Konto-Berechtigungen bleiben wirksam.","tags":["apiaccess"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID.","schema":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["api_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"api_012345abcdef_123456":{"id":"api_012345abcdef_123456","status":false,"permission":"permission_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitiveResponseFields":["api_key"],"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Apiaccess.php","method":"getApiaccess","line":150},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"apiaccess","short":"apia","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/apiaccess/{id} API-Zugang ändern Ändert status oder das konto-eigene Berechtigungsprofil eines vorhandenen API-Zugangs. api_key bleibt unverändert; ein neues Klartext-Secret wird hierbei nicht erzeugt. Die öffentliche Ziel-ID steht im Pfad. - Operation-ID: put_v2_apiaccess_id - Dokumentation: /docs/api/v2/operations/put-v2-apiaccess-id-29c3a1422a50/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID.","schema":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["api_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/apiaccess/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/apiaccess/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/apiaccess/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_apiaccess_id","summary":"API-Zugang ändern","description":"Ändert status oder das konto-eigene Berechtigungsprofil eines vorhandenen API-Zugangs. api_key bleibt unverändert; ein neues Klartext-Secret wird hierbei nicht erzeugt. Die öffentliche Ziel-ID steht im Pfad.","tags":["apiaccess"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche API-Zugang-ID mit Präfix api_; keine Datenbank-ID.","schema":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["api_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"apiaccess_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"api_key":false,"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"A typed public permission ID must decode positively and exist in firma_permission for this account. Wrong type, missing or foreign roles throw ED80100 with require.permission=false; no default/clear fallback on PUT.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["api_key"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["apiaccess_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"status":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Apiaccess.php","method":"updateApiaccess","line":117},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"apiaccess","short":"apia","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/apiaccess/list API-Zugänge auflisten Liest die im gewählten Konto verfügbaren API-Zugänge. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft. - Operation-ID: get_v2_apiaccess_list - Dokumentation: /docs/api/v2/operations/get-v2-apiaccess-list-b6f996338af6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/apiaccess/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/apiaccess/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/apiaccess/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_apiaccess_list","summary":"API-Zugänge auflisten","description":"Liest die im gewählten Konto verfügbaren API-Zugänge. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft.","tags":["apiaccess"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"apiaccess_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"api_012345abcdef_123456":{"id":"api_012345abcdef_123456","status":false,"permission":"permission_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^api_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36,"type":"boolean"},"api_key":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string","x-edoobox-sensitive":true},"permission":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Public permission v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook.","x-edoobox-contractSource":[{"file":"application/models/v2/Apiaccess.php","line":55,"lineEnd":64,"reason":"Input getter and output setter use the permission public ID type; PUT dispatches its dedicated hook.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccessfunc.php","line":6,"lineEnd":29,"reason":"Before insert validates explicit typed permission against normalized input and account ownership, or resolves a same-account default for omitted/legacy empty input.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccessfunc.php","line":31,"lineEnd":56,"reason":"PUT uses strict permission type and same-account existence query; invalid role throws ED80100 with require.permission=false.","sha256":"35bb0286908910683a9d5d56c0f5ac138d2b47d759417623cbd918bbc17479b2"},{"file":"application/models/v2/Apiaccess.php","line":79,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"application/models/v2/Apiaccess.php","line":117,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"4bf3c13ed924a1d30c97aed171de3fbce40697f633e7bad907dc514ea61b4c6b"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitiveResponseFields":["api_key"],"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Apiaccess.php","method":"getApiaccess","line":150},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"apiaccess","short":"apia","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/attendance Präsenzdatensatz löschen Löscht einen vorhandenen Präsenzdatensatz mit öffentlicher attendance-ID nach Prüfung der Kontozuordnung. Ohne Pfad-ID ist id im obersten Requestfeld erforderlich; die Aktion löscht nicht den Teilnehmer oder Termin. - Operation-ID: delete_v2_attendance - Dokumentation: /docs/api/v2/operations/delete-v2-attendance-9865fea909a0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "attendance_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/attendance' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "attendance_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"attendance_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_attendance","summary":"Präsenzdatensatz löschen","description":"Löscht einen vorhandenen Präsenzdatensatz mit öffentlicher attendance-ID nach Prüfung der Kontozuordnung. Ohne Pfad-ID ist id im obersten Requestfeld erforderlich; die Aktion löscht nicht den Teilnehmer oder Termin.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attendance_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["attendance_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["attendance_012345abcdef_123456"]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Präsenzdatensatz löschen","value":{"id":"attendance_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"deleteAttendance","line":162},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/attendance Präsenzstatus lesen Liest vorhandene Präsenzdatensätze je participant und date; type ist der konfigurierte Status. Die Pfad-ID setzt den id-Filter. Ohne Pfad-ID können etwa offer, date, participant und user im allgemeinen filter verwendet werden; fields, order und limit steuern die Sammlung. - Operation-ID: get_v2_attendance - Dokumentation: /docs/api/v2/operations/get-v2-attendance-ff7f57fd2b6f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/attendance' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_attendance","summary":"Präsenzstatus lesen","description":"Liest vorhandene Präsenzdatensätze je participant und date; type ist der konfigurierte Status. Die Pfad-ID setzt den id-Filter. Ohne Pfad-ID können etwa offer, date, participant und user im allgemeinen filter verwendet werden; fields, order und limit steuern die Sammlung.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"attendance_012345abcdef_123456":{"id":"attendance_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"getAttendance","line":178},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/attendance Präsenzstatus für einen Teilnehmer erfassen Erstellt Präsenz für date, participant und type. Pro Teilnehmer und Termin darf nur ein Datensatz bestehen; für Änderungen PUT verwenden. Termin und Teilnehmer müssen zum selben Angebot gehören. Eine Liste von Datensätzen ist im allgemeinen Insert ebenfalls möglich. - Operation-ID: post_v2_attendance - Dokumentation: /docs/api/v2/operations/post-v2-attendance-6bd600c04cd3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "date": "date_012345abcdef_123456", "participant": "participant_012345abcdef_123456", "type": 1 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/attendance' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "date": "date_012345abcdef_123456", "participant": "participant_012345abcdef_123456", "type": 1 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"date\":\"date_012345abcdef_123456\",\"participant\":\"participant_012345abcdef_123456\",\"type\":1}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_attendance","summary":"Präsenzstatus für einen Teilnehmer erfassen","description":"Erstellt Präsenz für date, participant und type. Pro Teilnehmer und Termin darf nur ein Datensatz bestehen; für Änderungen PUT verwenden. Termin und Teilnehmer müssen zum selben Angebot gehören. Eine Liste von Datensätzen ist im allgemeinen Insert ebenfalls möglich.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bei Einzelanlage data.id als öffentliche ID; bei Mehrfachanlage als ID-Liste. insert meldet den Schreibstatus.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"attendance_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"anyOf":[{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["attendance_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["attendance_012345abcdef_123456"]}},{"type":"null"}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"date":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":43},"last_change":{"readOnly":true,"description":"Getter ignores input and returns time(). insert_default injects it on omission when present; omission otherwise does not call the getter. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":68,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":82}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["date","type","participant"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"date":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":43},"last_change":{"readOnly":true,"description":"Getter ignores input and returns time(). insert_default injects it on omission when present; omission otherwise does not call the getter. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":68,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":82}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["date","type","participant"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Präsenzstatus für einen Teilnehmer erfassen","value":{"data":{"date":"date_012345abcdef_123456","participant":"participant_012345abcdef_123456","type":1}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"postAttendance","line":109},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/attendance Vorhandenen Präsenzstatus ändern Aktualisiert type eines bestehenden Präsenzdatensatzes und schreibt dabei einen Statusverlauf mit Bearbeiter. date und participant bleiben unveränderlich. Ohne Pfad-ID ist die öffentliche attendance-ID im obersten id-Feld erforderlich; last_change wird beim Statuswechsel serverseitig gesetzt. - Operation-ID: put_v2_attendance - Dokumentation: /docs/api/v2/operations/put-v2-attendance-7c78ae2413bf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "type": 1 }, "id": "attendance_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/attendance' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "type": 1 }, "id": "attendance_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"type\":1},\"id\":\"attendance_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_attendance","summary":"Vorhandenen Präsenzstatus ändern","description":"Aktualisiert type eines bestehenden Präsenzdatensatzes und schreibt dabei einen Statusverlauf mit Bearbeiter. date und participant bleiben unveränderlich. Ohne Pfad-ID ist die öffentliche attendance-ID im obersten id-Feld erforderlich; last_change wird beim Statuswechsel serverseitig gesetzt.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attendance_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["attendance_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"date":false,"last_change":false,"type":{"x-edoobox-canonicalType":"integer","description":"PHP int cast; integers are canonical. No rejecting wire type/range is checked; other JSON values also coerce.\n\nHook calls Structure int cast, then adds scan_time=time().\n\nRuntime preconditions / rejection: Missing attendance row makes callback false -> ED90100. No status enum/range check.","x-edoobox-schemaStatus":"source-reviewed","examples":[2],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"participant":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["date","last_change","participant"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Vorhandenen Präsenzstatus ändern","value":{"data":{"type":1},"id":"attendance_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"updateAttendance","line":145},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/attendance/{id} Präsenzdatensatz löschen Löscht einen vorhandenen Präsenzdatensatz mit öffentlicher attendance-ID nach Prüfung der Kontozuordnung. Ohne Pfad-ID ist id im obersten Requestfeld erforderlich; die Aktion löscht nicht den Teilnehmer oder Termin. - Operation-ID: delete_v2_attendance_id - Dokumentation: /docs/api/v2/operations/delete-v2-attendance-id-0f7776b28a83/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/attendance/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_attendance_id","summary":"Präsenzdatensatz löschen","description":"Löscht einen vorhandenen Präsenzdatensatz mit öffentlicher attendance-ID nach Prüfung der Kontozuordnung. Ohne Pfad-ID ist id im obersten Requestfeld erforderlich; die Aktion löscht nicht den Teilnehmer oder Termin.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attendance_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["attendance_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["attendance_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Präsenzdatensatz löschen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"deleteAttendance","line":162},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/attendance/{id} Präsenzstatus lesen Liest vorhandene Präsenzdatensätze je participant und date; type ist der konfigurierte Status. Die Pfad-ID setzt den id-Filter. Ohne Pfad-ID können etwa offer, date, participant und user im allgemeinen filter verwendet werden; fields, order und limit steuern die Sammlung. - Operation-ID: get_v2_attendance_id - Dokumentation: /docs/api/v2/operations/get-v2-attendance-id-5036f3f67389/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/attendance/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_attendance_id","summary":"Präsenzstatus lesen","description":"Liest vorhandene Präsenzdatensätze je participant und date; type ist der konfigurierte Status. Die Pfad-ID setzt den id-Filter. Ohne Pfad-ID können etwa offer, date, participant und user im allgemeinen filter verwendet werden; fields, order und limit steuern die Sammlung.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"attendance_012345abcdef_123456":{"id":"attendance_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"getAttendance","line":178},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/attendance/{id} Vorhandenen Präsenzstatus ändern Aktualisiert type eines bestehenden Präsenzdatensatzes und schreibt dabei einen Statusverlauf mit Bearbeiter. date und participant bleiben unveränderlich. Ohne Pfad-ID ist die öffentliche attendance-ID im obersten id-Feld erforderlich; last_change wird beim Statuswechsel serverseitig gesetzt. - Operation-ID: put_v2_attendance_id - Dokumentation: /docs/api/v2/operations/put-v2-attendance-id-c7d93ab04d31/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "type": 1 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/attendance/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "type": 1 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"type\":1}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_attendance_id","summary":"Vorhandenen Präsenzstatus ändern","description":"Aktualisiert type eines bestehenden Präsenzdatensatzes und schreibt dabei einen Statusverlauf mit Bearbeiter. date und participant bleiben unveränderlich. Ohne Pfad-ID ist die öffentliche attendance-ID im obersten id-Feld erforderlich; last_change wird beim Statuswechsel serverseitig gesetzt.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attendance_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["attendance_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"date":false,"last_change":false,"type":{"x-edoobox-canonicalType":"integer","description":"PHP int cast; integers are canonical. No rejecting wire type/range is checked; other JSON values also coerce.\n\nHook calls Structure int cast, then adds scan_time=time().\n\nRuntime preconditions / rejection: Missing attendance row makes callback false -> ED90100. No status enum/range check.","x-edoobox-schemaStatus":"source-reviewed","examples":[2],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"participant":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["date","last_change","participant"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Vorhandenen Präsenzstatus ändern","value":{"data":{"type":1}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"updateAttendance","line":145},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/attendance/bulk/bulk_attendance_date Präsenz für alle Teilnehmer eines Termins setzen Setzt type für alle benannten Teilnehmer des date-Termins und protokolliert die Änderungen. Bei Lektionenangeboten werden nur für diesen Termin gebuchte Teilnehmer verarbeitet. type wird als int normalisiert; anschliessend sind 0 bis 99 zulässig. Vorhandene Präsenz wird überschrieben, fehlende Datensätze werden angelegt. - Operation-ID: post_v2_attendance_bulk_bulk_attendance_date - Dokumentation: /docs/api/v2/operations/post-v2-attendance-bulk-bulk-attendance-date-b5584e6c3df7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "date": "date_012345abcdef_123456", "type": 1 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/attendance/bulk/bulk_attendance_date' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/bulk/bulk_attendance_date"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "date": "date_012345abcdef_123456", "type": 1 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/bulk/bulk_attendance_date" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"date\":\"date_012345abcdef_123456\",\"type\":1}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_attendance_bulk_bulk_attendance_date","summary":"Präsenz für alle Teilnehmer eines Termins setzen","description":"Setzt type für alle benannten Teilnehmer des date-Termins und protokolliert die Änderungen. Bei Lektionenangeboten werden nur für diesen Termin gebuchte Teilnehmer verarbeitet. type wird als int normalisiert; anschliessend sind 0 bis 99 zulässig. Vorhandene Präsenz wird überschrieben, fehlende Datensätze werden angelegt.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"bulk":"date_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"bulk":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["date_012345abcdef_123456"]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["date_012345abcdef_123456"]},"type":{"x-edoobox-canonicalType":"integer","description":"Nach int-Normalisierung muss der Wert 0 bis 99 sein; JSON-Integer 0 ist ausdrücklich zulässig."}},"additionalProperties":true,"required":["date","type"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Präsenz für alle Teilnehmer eines Termins setzen","value":{"data":{"date":"date_012345abcdef_123456","type":1}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Attendance.php","line":124,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"postAttendance","line":109,"branch":{"file":"application/models/v2/Attendance.php","line":124}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/attendance/list Präsenzstatus lesen Liest vorhandene Präsenzdatensätze je participant und date; type ist der konfigurierte Status. Die Pfad-ID setzt den id-Filter. Ohne Pfad-ID können etwa offer, date, participant und user im allgemeinen filter verwendet werden; fields, order und limit steuern die Sammlung. - Operation-ID: get_v2_attendance_list - Dokumentation: /docs/api/v2/operations/get-v2-attendance-list-73d5d49ab11a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/attendance/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_attendance_list","summary":"Präsenzstatus lesen","description":"Liest vorhandene Präsenzdatensätze je participant und date; type ist der konfigurierte Status. Die Pfad-ID setzt den id-Filter. Ohne Pfad-ID können etwa offer, date, participant und user im allgemeinen filter verwendet werden; fields, order und limit steuern die Sammlung.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"attendance_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"attendance_012345abcdef_123456":{"id":"attendance_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["attendance_012345abcdef_123456"]},"date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"last_change":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":57,"lineStart":57,"lineEnd":67,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"5e27196ad7a6d0bd57648d5ab41c77ae47bfd3c3cd9d19cdde6e7723efccd2d1","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"type":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Attendance.php","line":68,"lineStart":68,"lineEnd":81,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"41125089feddacf188b71610cb39d2b83f18945b124795aba38706a1f0db24fa","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Attendancefunc.php","line":24,"lineStart":24,"lineEnd":55,"sha256":"ac2e9ccc3d95fa1d1d3459f43d850ccf4d45915dea576a4afd711adec5f0bf3c","spanSha256":"a34a276bcaf3ae2f40914badc74a12d8f605e171f4af6b6b5a0b8a6b2cbd1ff1","reason":"func delegates to _changeType"},{"file":"application/models/v2/Attendance.php","line":109,"lineStart":109,"lineEnd":152,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","spanSha256":"bb5e60a6138346ecdb5b10e2b8c3ff68f063f87491cbd6c6b46e362d7284a7e1","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"participant":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["participant_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"getAttendance","line":178},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/attendance/list/bulk_attendance_date Präsenz für alle Teilnehmer eines Termins setzen Setzt type für alle benannten Teilnehmer des date-Termins und protokolliert die Änderungen. Bei Lektionenangeboten werden nur für diesen Termin gebuchte Teilnehmer verarbeitet. type wird als int normalisiert; anschliessend sind 0 bis 99 zulässig. Vorhandene Präsenz wird überschrieben, fehlende Datensätze werden angelegt. - Operation-ID: post_v2_attendance_list_bulk_attendance_date - Dokumentation: /docs/api/v2/operations/post-v2-attendance-list-bulk-attendance-date-ad948a8bd2d4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "date": "date_012345abcdef_123456", "type": 1 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/attendance/list/bulk_attendance_date' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/list/bulk_attendance_date"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "date": "date_012345abcdef_123456", "type": 1 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/list/bulk_attendance_date" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"date\":\"date_012345abcdef_123456\",\"type\":1}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_attendance_list_bulk_attendance_date","summary":"Präsenz für alle Teilnehmer eines Termins setzen","description":"Setzt type für alle benannten Teilnehmer des date-Termins und protokolliert die Änderungen. Bei Lektionenangeboten werden nur für diesen Termin gebuchte Teilnehmer verarbeitet. type wird als int normalisiert; anschliessend sind 0 bis 99 zulässig. Vorhandene Präsenz wird überschrieben, fehlende Datensätze werden angelegt.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"bulk":"date_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"bulk":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["date_012345abcdef_123456"]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["date_012345abcdef_123456"]},"type":{"x-edoobox-canonicalType":"integer","description":"Nach int-Normalisierung muss der Wert 0 bis 99 sein; JSON-Integer 0 ist ausdrücklich zulässig."}},"additionalProperties":true,"required":["date","type"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Präsenz für alle Teilnehmer eines Termins setzen","value":{"data":{"date":"date_012345abcdef_123456","type":1}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"postAttendance","line":109,"branch":{"file":"application/models/v2/Attendance.php","line":124}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/attendance/list/bulk_attendance_offer Präsenz für alle Termine eines Angebots setzen Setzt type bei benannten Teilnehmern für die Termine von offer; bei Lektionenangeboten nur für gebuchte Termine. Schreibt vorhandene Präsenz um und ergänzt fehlende Datensätze samt Verlauf. type wird als int normalisiert auf 0 bis 99 geprüft. Bei normalen Angeboten darf die Kombination aus Teilnehmern und Terminen höchstens 20000 Einträge umfassen. - Operation-ID: post_v2_attendance_list_bulk_attendance_offer - Dokumentation: /docs/api/v2/operations/post-v2-attendance-list-bulk-attendance-offer-250fbb31776e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "type": 1 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/attendance/list/bulk_attendance_offer' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/list/bulk_attendance_offer"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "type": 1 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/list/bulk_attendance_offer" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"type\":1}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_attendance_list_bulk_attendance_offer","summary":"Präsenz für alle Termine eines Angebots setzen","description":"Setzt type bei benannten Teilnehmern für die Termine von offer; bei Lektionenangeboten nur für gebuchte Termine. Schreibt vorhandene Präsenz um und ergänzt fehlende Datensätze samt Verlauf. type wird als int normalisiert auf 0 bis 99 geprüft. Bei normalen Angeboten darf die Kombination aus Teilnehmern und Terminen höchstens 20000 Einträge umfassen.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"bulk":"offer_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"bulk":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"type":{"x-edoobox-canonicalType":"integer","description":"Nach int-Normalisierung muss der Wert 0 bis 99 sein; JSON-Integer 0 ist ausdrücklich zulässig."}},"additionalProperties":true,"required":["offer","type"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Präsenz für alle Termine eines Angebots setzen","value":{"data":{"offer":"offer_012345abcdef_123456","type":1}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"postAttendance","line":109,"branch":{"file":"application/models/v2/Attendance.php","line":127}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/attendance/list/I_want_demo_data Demo-Präsenz für Entwicklung erzeugen Ergänzt nur in der Entwicklungsumgebung fehlende Präsenzdatensätze für offer. types liefert mögliche Statuswerte für die Zufallsauswahl; ohne nicht leere Liste werden 1 bis 5 verwendet. Der Produktionsserver lehnt die Aktion mit only_development_environment ab. - Operation-ID: post_v2_attendance_list_I_want_demo_data - Dokumentation: /docs/api/v2/operations/post-v2-attendance-list-i-want-demo-data-c29262cb40e8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "types": [ 1, 2, 3 ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/attendance/list/I_want_demo_data' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/list/I_want_demo_data"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "types": [ 1, 2, 3 ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/list/I_want_demo_data" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"types\":[1,2,3]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_attendance_list_I_want_demo_data","summary":"Demo-Präsenz für Entwicklung erzeugen","description":"Ergänzt nur in der Entwicklungsumgebung fehlende Präsenzdatensätze für offer. types liefert mögliche Statuswerte für die Zufallsauswahl; ohne nicht leere Liste werden 1 bis 5 verwendet. Der Produktionsserver lehnt die Aktion mit only_development_environment ab.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nur in Entwicklung: Anzahl ergänzter Präsenzdatensätze; Produktion liefert einen Fehler.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"attendances":12}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"attendances":{"type":"integer"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"types":{"anyOf":[{"type":"array","items":{"x-edoobox-canonicalType":"integer","description":"PHP int-Normalisierung; ein Integer ist die kanonische Eingabe, numerischer Text wird ebenfalls normalisiert. Der erlaubte Bereich wird nach der Umwandlung geprüft."}},{"type":"object","additionalProperties":{"x-edoobox-canonicalType":"integer","description":"PHP int-Normalisierung; ein Integer ist die kanonische Eingabe, numerischer Text wird ebenfalls normalisiert. Der erlaubte Bereich wird nach der Umwandlung geprüft."}}]}},"additionalProperties":true,"required":["offer"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Demo-Präsenz für Entwicklung erzeugen","value":{"data":{"offer":"offer_012345abcdef_123456","types":[1,2,3]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"postAttendance","line":109,"branch":{"file":"application/models/v2/Attendance.php","line":114}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/attendance/list/log Änderungsverlauf der Präsenz lesen Liest den Präsenzverlauf mit attendance-ID, öffentlicher admin-ID (oder false), date_create und type. Die Filter beziehen sich hier auf Verlaufsfelder, nicht auf die normalen Präsenzfelder; etwa attendance grenzt die Historie eines Datensatzes ein. - Operation-ID: get_v2_attendance_list_log - Dokumentation: /docs/api/v2/operations/get-v2-attendance-list-log-21ee2f289e20/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | Optionale Filter auf Verlaufsfelder id, attendance, admin, date_create oder type. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Optionale Filter auf Verlaufsfelder id, attendance, admin, date_create oder type.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"attendance","value":"attendance_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/attendance/list/log' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/list/log"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/list/log" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_attendance_list_log","summary":"Änderungsverlauf der Präsenz lesen","description":"Liest den Präsenzverlauf mit attendance-ID, öffentlicher admin-ID (oder false), date_create und type. Die Filter beziehen sich hier auf Verlaufsfelder, nicht auf die normalen Präsenzfelder; etwa attendance grenzt die Historie eines Datensatzes ein.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Optionale Filter auf Verlaufsfelder id, attendance, admin, date_create oder type.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"attendance","value":"attendance_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"attendancelog_012345abcdef_123456":{"id":"attendancelog_012345abcdef_123456","attendance":"attendance_012345abcdef_123456","admin":"admin_012345abcdef_123456","date_create":"2026-10-22T10:00:00+02:00","type":1}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendancelog_[a-f0-9]{12}_-?[0-9]+$","examples":["attendancelog_012345abcdef_123456"]},"attendance":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"admin":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"]},{"const":false}]},"date_create":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"type":{"type":"integer"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendancelog_[a-f0-9]{12}_-?[0-9]+$","examples":["attendancelog_012345abcdef_123456"]},"attendance":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"admin":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"]},{"const":false}]},"date_create":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"type":{"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"getAttendance","line":178,"branch":{"file":"application/models/v2/Attendance.php","line":250}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/attendance/list/table Teilnehmer-Präsenzmatrix laden Lädt Teilnehmer, Präsenz und Termine für offer. filter muss offer sowie date (kommagetrennte Termin-IDs) oder eine Navigation über date_after beziehungsweise date_before enthalten. Die Navigation ergänzt höchstens fünf Termine neben dem angegebenen Referenztermin. Bei Lektionenbuchungen wird date_participants geliefert; limit begrenzt Teilnehmer und wird auf höchstens 300 Einträge normalisiert. - Operation-ID: get_v2_attendance_list_table - Dokumentation: /docs/api/v2/operations/get-v2-attendance-list-table-0a73d5c5d712/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | offer ist erforderlich. Termine über date (ein Text mit kommagetrennten date-IDs) oder Navigation date_after/date_before auswählen. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"offer ist erforderlich. Termine über date (ein Text mit kommagetrennten date-IDs) oder Navigation date_after/date_before auswählen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"offer"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}],"anyOf":[{"contains":{"type":"object","properties":{"property":{"const":"date"},"value":{"type":"string"}},"additionalProperties":true,"required":["property","value"]}},{"contains":{"type":"object","properties":{"property":{"const":"date_after"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}},{"contains":{"type":"object","properties":{"property":{"const":"date_before"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"offer","value":"offer_012345abcdef_123456"},{"property":"date","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/attendance/list/table' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/attendance/list/table"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/attendance/list/table" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_attendance_list_table","summary":"Teilnehmer-Präsenzmatrix laden","description":"Lädt Teilnehmer, Präsenz und Termine für offer. filter muss offer sowie date (kommagetrennte Termin-IDs) oder eine Navigation über date_after beziehungsweise date_before enthalten. Die Navigation ergänzt höchstens fünf Termine neben dem angegebenen Referenztermin. Bei Lektionenbuchungen wird date_participants geliefert; limit begrenzt Teilnehmer und wird auf höchstens 300 Einträge normalisiert.","tags":["attendance"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"offer ist erforderlich. Termine über date (ein Text mit kommagetrennten date-IDs) oder Navigation date_after/date_before auswählen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"offer"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}],"anyOf":[{"contains":{"type":"object","properties":{"property":{"const":"date"},"value":{"type":"string"}},"additionalProperties":true,"required":["property","value"]}},{"contains":{"type":"object","properties":{"property":{"const":"date_after"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}},{"contains":{"type":"object","properties":{"property":{"const":"date_before"},"value":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"offer","value":"offer_012345abcdef_123456"},{"property":"date","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält participants, attendances und dates; date_participants nur bei Lektionenangeboten. limit mit start/reply/total steht neben data.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"participants":[],"attendances":[],"dates":[{"id":"date_012345abcdef_123456","description":"Vormittag","date_start":"2026-11-02T09:00:00+01:00","date_end":"2026-11-02T12:00:00+01:00"}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"participants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"company":{"type":"string"},"phone_number":{"type":"string"},"email":{"type":"string"},"pricecategory_name":{"type":"string"}},"additionalProperties":true}},"attendances":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^attendance_[a-f0-9]{12}_-?[0-9]+$","examples":["attendance_012345abcdef_123456"]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"last_change":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"type":{"type":"integer"}},"additionalProperties":true}},"date_participants":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]}},"additionalProperties":true}},"dates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]},"description":{"type":"string"},"date_start":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"date_end":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/7","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Attendance.php","line":253,"lineEnd":256,"sha256":"3b6e39b9c7ee81764713b3dfd93e890c0c9cdb5459b4bae93047d785a5361f1d","reason":"The exact table action dispatches to Attendancetable::getTableData."},"reason":"Builds the attendance table from stored dates, offers and participant/attendance rows.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/7","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Attendance.php","method":"getAttendance","line":178,"branch":{"file":"application/models/v2/Attendance.php","line":253}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"attendance","short":"atte","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/auth Login-Sitzung sperren und entfernen Entfernt den gewählten Login-Eintrag und sperrt seinen edid-Kontext über den Lösch-Hook. Erfordert auth-Level 4; die Sitzung wird nicht über eine rohe Datenbank-ID adressiert. - Operation-ID: delete_v2_auth - Dokumentation: /docs/api/v2/operations/delete-v2-auth-8afe31840763/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Alternative zum id-Feld im JSON-Body. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Alternative zum id-Feld im JSON-Body.","x-edoobox-id-kind":"login-context"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "edid_synthetic_login_context" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/auth' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "edid_synthetic_login_context" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"edid_synthetic_login_context\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_auth","summary":"Login-Sitzung sperren und entfernen","description":"Entfernt den gewählten Login-Eintrag und sperrt seinen edid-Kontext über den Lösch-Hook. Erfordert auth-Level 4; die Sitzung wird nicht über eine rohe Datenbank-ID adressiert.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Alternative zum id-Feld im JSON-Body.","x-edoobox-id-kind":"login-context"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"auth_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Login-Sitzung sperren und entfernen","value":{"id":"edid_synthetic_login_context"}}}}}},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"deleteAuth","line":256},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/auth Login-Sitzungen prüfen Liest Login-Sitzungen und öffentliche Metadaten im gewählten Konto. grant-type=access_token muss für diese GET-auth-Route ausdrücklich gesetzt sein; Bearer-Token bzw. Sitzungscookie und edid bleiben erforderlich. auth-Leserechte werden zusätzlich geprüft. Die id einer Sitzung ist der opake edid-Kontext, keine v2-Objekt-ID. - Operation-ID: get_v2_auth - Dokumentation: /docs/api/v2/operations/get-v2-auth-7c13933ce678/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | grant-type | header | ja | Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"grant-type","in":"header","required":true,"schema":{"type":"string","const":"access_token"},"description":"Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${HEADER_GRANT_TYPE:?HEADER_GRANT_TYPE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'grant-type: '"${HEADER_GRANT_TYPE}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers["grant-type"] = "" + required("HEADER_GRANT_TYPE"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["grant-type"] = "" + os.environ["HEADER_GRANT_TYPE"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth","summary":"Login-Sitzungen prüfen","description":"Liest Login-Sitzungen und öffentliche Metadaten im gewählten Konto. grant-type=access_token muss für diese GET-auth-Route ausdrücklich gesetzt sein; Bearer-Token bzw. Sitzungscookie und edid bleiben erforderlich. auth-Leserechte werden zusätzlich geprüft. Die id einer Sitzung ist der opake edid-Kontext, keine v2-Objekt-ID.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"grant-type","in":"header","required":true,"schema":{"type":"string","const":"access_token"},"description":"Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edid_synthetic_login_context":{"id":"edid_synthetic_login_context"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"getAuth","line":278},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/auth Administrator anmelden oder API-Token beziehen Meldet einen Administrator mit email/password oder einen nicht öffentlichen API-Zugang mit key/secret an (grant-type=password). Alternativ signiert authorization_code einen 50-Zeichen-Übergabetoken samt zugelassenem App-Callback für den anschließenden Browser-Login. Header grant-type hat Vorrang vor dem gleichnamigen JSON-Feld. Administratoren mit zweitem Faktor erhalten zunächst einen eingeschränkten 2FA-Kontext. API-Logins liefern access_token oben im Antwortobjekt, Administratoren auf dem Produktionssystem üblicherweise ein _edlogin-Cookie. Konto-Kontexte in data.login.data werden später über edid gewählt; Passwörter/Secrets/Token nie protokollieren. - Operation-ID: post_v2_auth - Dokumentation: /docs/api/v2/operations/post-v2-auth-307d710f787a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | grant-type | header | nein | Alternativ zum JSON-Feld; wird zuerst ausgewertet. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"grant-type","in":"header","required":false,"schema":{"type":"string","enum":["password","authorization_code"]},"description":"Alternativ zum JSON-Feld; wird zuerst ausgewertet."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "grant-type": "password", "email": "alex@example.invalid", "password": "Synthetic-Password-42!", "reply": "cookie", "remember": false } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "grant-type": "password", "email": "alex@example.invalid", "password": "Synthetic-Password-42!", "reply": "cookie", "remember": false }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"grant-type\":\"password\",\"email\":\"alex@example.invalid\",\"password\":\"Synthetic-Password-42!\",\"reply\":\"cookie\",\"remember\":false}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth","summary":"Administrator anmelden oder API-Token beziehen","description":"Meldet einen Administrator mit email/password oder einen nicht öffentlichen API-Zugang mit key/secret an (grant-type=password). Alternativ signiert authorization_code einen 50-Zeichen-Übergabetoken samt zugelassenem App-Callback für den anschließenden Browser-Login. Header grant-type hat Vorrang vor dem gleichnamigen JSON-Feld. Administratoren mit zweitem Faktor erhalten zunächst einen eingeschränkten 2FA-Kontext. API-Logins liefern access_token oben im Antwortobjekt, Administratoren auf dem Produktionssystem üblicherweise ein _edlogin-Cookie. Konto-Kontexte in data.login.data werden später über edid gewählt; Passwörter/Secrets/Token nie protokollieren.","tags":["auth"],"security":[],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"grant-type","in":"header","required":false,"schema":{"type":"string","enum":["password","authorization_code"]},"description":"Alternativ zum JSON-Feld; wird zuerst ausgewertet."}],"responses":{"200":{"description":"Je nach Grant Login-Kontext mit optionalem Login-Payload, Cookie-/Tokenmodus oder {state,redirect_uri} für App-Übergabe; API-access_token steht auf oberster Ebene. reply=code kann ausschließlich {code} liefern.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edid":"edid_synthetic_login_context","cookie":"_edlogin","token":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"edid":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"login":{"type":"object","additionalProperties":true},"token":{"type":["string","boolean"],"x-edoobox-sensitive":true},"cookie":{"type":["string","boolean"]},"code":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"state":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"redirect_uri":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"grant-type":{"type":"string","enum":["password","authorization_code"]},"email":{"type":"string","format":"email"},"password":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":8},"key":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"API-Schlüssel mit Präfix key_; aus dem geschützten API-Zugang beziehen."},"secret":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"API-Secret mit Präfix sec_; nur bei Erstellung ausgegeben."},"token":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":50,"maxLength":50},"redirect_uri":{"type":"string","description":"App-Callback beginnend mit com.edoobox.admission://, com.edoobox.scan:// oder com.edoobox.event://."},"reply":{"type":"string","description":"cookie (Standard), code (App-Übergabe) oder token (Administrator-Tokenantwort ist umgebungsabhängig eingeschränkt)."},"remember":{"description":"PHP-normalisierte Remember-Auswahl; kanonisch Boolean. true verlängert Administrator-Sitzungen.","examples":[false]},"expire":{"type":["string","number"],"description":"Optionaler API-Ablaufzeitpunkt, durch dP in Unixzeit normalisiert."},"state":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Signierter State aus dem authorization_code-Vorlauf bei reply=code."},"g-recaptcha-response":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Bedingt erforderlicher reCAPTCHA-Nachweis nach verdächtigen Anmeldeversuchen."}},"additionalProperties":true,"anyOf":[{"required":["email","password"]},{"required":["key","secret"]},{"required":["token","redirect_uri"]}],"description":"grant-type muss im Header oder JSON stehen. Das JSON-Schema kann die Header-Alternative nicht als erforderliches Body-Feld ausdrücken.","allOf":[{"if":{"required":["grant-type"],"properties":{"grant-type":{"const":"password"}}},"then":{"anyOf":[{"required":["email","password"]},{"required":["key","secret"]}]}},{"if":{"required":["grant-type"],"properties":{"grant-type":{"const":"authorization_code"}}},"then":{"required":["token","redirect_uri"]}}]},"examples":{"request":{"summary":"Administrator anmelden oder API-Token beziehen","value":{"grant-type":"password","email":"alex@example.invalid","password":"Synthetic-Password-42!","reply":"cookie","remember":false}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":false,"levels":[],"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme."}} ``` --- # DELETE /v2/auth/{id} Login-Sitzung sperren und entfernen Entfernt den gewählten Login-Eintrag und sperrt seinen edid-Kontext über den Lösch-Hook. Erfordert auth-Level 4; die Sitzung wird nicht über eine rohe Datenbank-ID adressiert. - Operation-ID: delete_v2_auth_id - Dokumentation: /docs/api/v2/operations/delete-v2-auth-id-f5f3b41aaf33/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Opaker Login-Kontext. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | query | nein | Alternative zum id-Feld im JSON-Body. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Opaker Login-Kontext.","schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"x-edoobox-id-kind":"login-context"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Alternative zum id-Feld im JSON-Body.","x-edoobox-id-kind":"login-context"}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/auth/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_auth_id","summary":"Login-Sitzung sperren und entfernen","description":"Entfernt den gewählten Login-Eintrag und sperrt seinen edid-Kontext über den Lösch-Hook. Erfordert auth-Level 4; die Sitzung wird nicht über eine rohe Datenbank-ID adressiert.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Opaker Login-Kontext.","schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"x-edoobox-id-kind":"login-context"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Alternative zum id-Feld im JSON-Body.","x-edoobox-id-kind":"login-context"}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"auth_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]}},"additionalProperties":true},"examples":{"request":{"summary":"Login-Sitzung sperren und entfernen","value":{}}}}}},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"deleteAuth","line":256},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/auth/{id} Login-Sitzungen prüfen Liest Login-Sitzungen und öffentliche Metadaten im gewählten Konto. grant-type=access_token muss für diese GET-auth-Route ausdrücklich gesetzt sein; Bearer-Token bzw. Sitzungscookie und edid bleiben erforderlich. auth-Leserechte werden zusätzlich geprüft. Die id einer Sitzung ist der opake edid-Kontext, keine v2-Objekt-ID. - Operation-ID: get_v2_auth_id - Dokumentation: /docs/api/v2/operations/get-v2-auth-id-a64f955dde76/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Opaker Login-Kontext aus der Auth-Antwort. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | grant-type | header | ja | Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Opaker Login-Kontext aus der Auth-Antwort.","schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"x-edoobox-id-kind":"login-context"},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"grant-type","in":"header","required":true,"schema":{"type":"string","const":"access_token"},"description":"Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${HEADER_GRANT_TYPE:?HEADER_GRANT_TYPE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'grant-type: '"${HEADER_GRANT_TYPE}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers["grant-type"] = "" + required("HEADER_GRANT_TYPE"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["grant-type"] = "" + os.environ["HEADER_GRANT_TYPE"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_id","summary":"Login-Sitzungen prüfen","description":"Liest Login-Sitzungen und öffentliche Metadaten im gewählten Konto. grant-type=access_token muss für diese GET-auth-Route ausdrücklich gesetzt sein; Bearer-Token bzw. Sitzungscookie und edid bleiben erforderlich. auth-Leserechte werden zusätzlich geprüft. Die id einer Sitzung ist der opake edid-Kontext, keine v2-Objekt-ID.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Opaker Login-Kontext aus der Auth-Antwort.","schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"x-edoobox-id-kind":"login-context"},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"grant-type","in":"header","required":true,"schema":{"type":"string","const":"access_token"},"description":"Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edid_synthetic_login_context":{"id":"edid_synthetic_login_context"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitiveResponseFields":["log_data"],"x-edoobox-sensitiveResponseSource":{"file":"application/models/v2/Auth.php","line":144,"reason":"The public serializer whitelists device metadata and removes support-user IDs and referrer URLs. MCP excludes the whole log_data value from own-login reads before model or result-artifact exposure; business tools do not need browser session metadata."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"getAuth","line":278},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/{id}/2fa Zweiten Faktor für die Anmeldung bestätigen Bei einem noch eingeschränkten Login-Kontext prüft die Route den zweiten Faktor und erneuert bei Erfolg das Login. type=otpauth/backupkey benötigt code, yubikey benötigt yubikey, phone sendet mit auth2fa+delivery einen Code und prüft ihn anschließend mit review+code. Passkey verwendet action=options und danach action=verify mit ceremony+credential; browsergebundene Challenge und User-Verifikation werden geprüft. Für bereits vollständig authentifizierte Logins bestätigt die Modellroute lediglich {id,2fa:true}. Ein /list-Alias benötigt bei normaler Modellverarbeitung id; für den vorgeschalteten 2FA-Handshake ist der Header-edid maßgeblich. - Operation-ID: put_v2_auth_id_2fa - Dokumentation: /docs/api/v2/operations/put-v2-auth-id-2fa-8c1e61d4f995/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Login-Kontext der bestehenden Sitzung. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Login-Kontext der bestehenden Sitzung.","x-edoobox-id-kind":"login-context"}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "type": "otpauth", "code": "123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/'"${PATH_ID}"'/2fa' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/{id}/2fa"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "type": "otpauth", "code": "123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/{id}/2fa" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"type\":\"otpauth\",\"code\":\"123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_id_2fa","summary":"Zweiten Faktor für die Anmeldung bestätigen","description":"Bei einem noch eingeschränkten Login-Kontext prüft die Route den zweiten Faktor und erneuert bei Erfolg das Login. type=otpauth/backupkey benötigt code, yubikey benötigt yubikey, phone sendet mit auth2fa+delivery einen Code und prüft ihn anschließend mit review+code. Passkey verwendet action=options und danach action=verify mit ceremony+credential; browsergebundene Challenge und User-Verifikation werden geprüft. Für bereits vollständig authentifizierte Logins bestätigt die Modellroute lediglich {id,2fa:true}. Ein /list-Alias benötigt bei normaler Modellverarbeitung id; für den vorgeschalteten 2FA-Handshake ist der Header-edid maßgeblich.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Login-Kontext der bestehenden Sitzung.","x-edoobox-id-kind":"login-context"}],"responses":{"200":{"description":"Bei bestehender Authentifizierung {id,2fa:true}; im 2FA-Handshake Challenge-/Review-Daten oder erneuerte Login-Antwort. Geschütztes Material nicht protokollieren.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"edid_synthetic_login_context","2fa":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"2fa":{"type":"boolean"}},"additionalProperties":true},{"type":"object","properties":{"ceremony":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"options":{"type":"object","additionalProperties":true}},"additionalProperties":true},{"type":"object","properties":{"review":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"valid":{"type":"string"}},"additionalProperties":true},{"type":"object","additionalProperties":true}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Auth.php","line":210,"reason":"Legacy branch selected by segment4; the third segment is the existing login context, not a database ID. Current source returns the 2fa state acknowledgment."},"x-edoobox-bodylessSource":{"file":"application/models/v2/Auth.php","line":210},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"enum":["otpauth","backupkey","yubikey","phone","passkey"]},"code":{"type":["string","integer"],"writeOnly":true,"x-edoobox-sensitive":true},"yubikey":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"auth2fa":{"type":"string","pattern":"^auth2fa_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"delivery":{"type":"string","description":"call wählt Sprachanruf; alle anderen Werte werden als sms behandelt."},"review":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"action":{"enum":["options","verify"]},"ceremony":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"credential":{"type":"object","additionalProperties":true,"x-edoobox-sensitive":true,"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]+$","x-edoobox-id-kind":"webauthn-credential","description":"Base64URL-codierte Authenticator-Credential-Bytes; keine edoobox-Objekt- oder Datenbank-ID.","examples":["c3ludGhldGljLWNyZWRlbnRpYWw"]},"rawId":{"type":"string"},"type":{"type":"string"},"response":{"type":"object","additionalProperties":true},"clientExtensionResults":{"type":"object","additionalProperties":true}},"writeOnly":true},"reply":{"type":"string"}},"additionalProperties":true,"anyOf":[{"type":"object","properties":{"type":{"const":"otpauth"}},"additionalProperties":true,"required":["type","code"]},{"type":"object","properties":{"type":{"const":"backupkey"}},"additionalProperties":true,"required":["type","code"]},{"type":"object","properties":{"type":{"const":"yubikey"}},"additionalProperties":true,"required":["type","yubikey"]},{"type":"object","properties":{"type":{"const":"phone"}},"additionalProperties":true,"required":["type","auth2fa","delivery"]},{"type":"object","properties":{"type":{"const":"phone"}},"additionalProperties":true,"required":["type","review","code"]},{"type":"object","properties":{"type":{"const":"passkey"},"action":{"const":"options"}},"additionalProperties":true,"required":["type","action"]},{"type":"object","properties":{"type":{"const":"passkey"},"action":{"const":"verify"}},"additionalProperties":true,"required":["type","action","ceremony","credential"]}]},"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zweiten Faktor für die Anmeldung bestätigen","value":{"data":{"type":"otpauth","code":"123456"}}}}}},"required":true},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":227}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/{id}/logout Alle Login-Kontexte abmelden Meldet alle noch gültigen Login-Kontexte des aktuellen Administrator-Cookies ab, sperrt ihre edid-Werte und löscht das Login-Cookie. Falls kein Multi-Login-Cookie vorliegt, wird der gewählte Kontext abgemeldet. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen. - Operation-ID: put_v2_auth_id_logout - Dokumentation: /docs/api/v2/operations/put-v2-auth-id-logout-65b94ea621ab/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Opaker Ziel-Login-Kontext. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Opaker Ziel-Login-Kontext.","x-edoobox-id-kind":"login-context"}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/'"${PATH_ID}"'/logout' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/{id}/logout"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/{id}/logout" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_id_logout","summary":"Alle Login-Kontexte abmelden","description":"Meldet alle noch gültigen Login-Kontexte des aktuellen Administrator-Cookies ab, sperrt ihre edid-Werte und löscht das Login-Cookie. Falls kein Multi-Login-Cookie vorliegt, wird der gewählte Kontext abgemeldet. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Opaker Ziel-Login-Kontext.","x-edoobox-id-kind":"login-context"}],"responses":{"200":{"description":"Gewählter Kontext, logout=true und Liste aller abgemeldeten edid-Werte.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edid":"edid_synthetic_login_context","logout":true,"logout_edids":["edid_synthetic_login_context"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"edid":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"logout":{"type":"boolean"},"logout_edids":{"type":"array","items":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]}}},"additionalProperties":true,"required":["edid","logout","logout_edids"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"data":{"type":"object","properties":{"expire":{"type":["string","number"]},"reply":{"type":"string"},"remember":{"description":"Kanonisch Boolean; native PHP-Normalisierung gilt."}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Alle Login-Kontexte abmelden","value":{"data":{}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Auth.php","line":201,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":218}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/{id}/logout_edid Einzelnen Login-Kontext abmelden Meldet nur den gewählten edid-Kontext ab. Verbleibende gültige Kontexte werden in ein erneuertes Cookie übernommen; beim letzten Kontext wird das Cookie gelöscht. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen. - Operation-ID: put_v2_auth_id_logout_edid - Dokumentation: /docs/api/v2/operations/put-v2-auth-id-logout-edid-8785fd418c70/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Opaker Ziel-Login-Kontext. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Opaker Ziel-Login-Kontext.","x-edoobox-id-kind":"login-context"}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/'"${PATH_ID}"'/logout_edid' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/{id}/logout_edid"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/{id}/logout_edid" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_id_logout_edid","summary":"Einzelnen Login-Kontext abmelden","description":"Meldet nur den gewählten edid-Kontext ab. Verbleibende gültige Kontexte werden in ein erneuertes Cookie übernommen; beim letzten Kontext wird das Cookie gelöscht. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Opaker Ziel-Login-Kontext.","x-edoobox-id-kind":"login-context"}],"responses":{"200":{"description":"Letzter Kontext: Logout-Bestätigung; andernfalls erneuerte Login-Antwort mit verbleibendem edid und logout_edids.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edid":"edid_synthetic_login_context","logout":true,"logout_edids":["edid_synthetic_login_context"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"edid":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"logout":{"type":"boolean"},"logout_edids":{"type":"array","items":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]}},"login":{"type":"object","additionalProperties":true},"token":{"type":["boolean","string"],"x-edoobox-sensitive":true},"cookie":{"type":["boolean","string"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"data":{"type":"object","properties":{"expire":{"type":["string","number"]},"reply":{"type":"string"},"remember":{"description":"Kanonisch Boolean; native PHP-Normalisierung gilt."}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Einzelnen Login-Kontext abmelden","value":{"data":{}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Auth.php","line":204,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":221}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/{id}/refresh Login-Sitzung verlängern Verlängert die aktuelle, weiterhin gültige Sitzung und prüft Rechte und Kontozugehörigkeit erneut. Administratoren benötigen gegebenenfalls eine bereits abgeschlossene 2FA; remember bestimmt 30 Tage bzw. 30 Minuten, API-Logins können data.expire verwenden. Der aktuelle Header-edid bestimmt den zu erneuernden Kontext. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen. - Operation-ID: put_v2_auth_id_refresh - Dokumentation: /docs/api/v2/operations/put-v2-auth-id-refresh-131d403b7f1b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Opaker Ziel-Login-Kontext. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Opaker Ziel-Login-Kontext.","x-edoobox-id-kind":"login-context"}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/'"${PATH_ID}"'/refresh' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/{id}/refresh"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/{id}/refresh" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_id_refresh","summary":"Login-Sitzung verlängern","description":"Verlängert die aktuelle, weiterhin gültige Sitzung und prüft Rechte und Kontozugehörigkeit erneut. Administratoren benötigen gegebenenfalls eine bereits abgeschlossene 2FA; remember bestimmt 30 Tage bzw. 30 Minuten, API-Logins können data.expire verwenden. Der aktuelle Header-edid bestimmt den zu erneuernden Kontext. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"],"x-edoobox-id-kind":"login-context"},"description":"Opaker Ziel-Login-Kontext.","x-edoobox-id-kind":"login-context"}],"responses":{"200":{"description":"Erneuerte grantabhängige Login-Antwort mit edid und login, Cookie bzw. API-access_token; Fehler bei abgelaufenem oder gesperrtem Kontext.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"auth_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"data":{"type":"object","properties":{"expire":{"type":["string","number"]},"reply":{"type":"string"},"remember":{"description":"Kanonisch Boolean; native PHP-Normalisierung gilt."}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Login-Sitzung verlängern","value":{"data":{}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Auth.php","line":207,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":224}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/auth/access_token App-Anmeldecode gegen Access-Token tauschen Liest code und den zugehörigen 50-Zeichen-token direkt aus einem JSON-Body auf oberster Ebene, auch bei GET. Der Code muss aus dem vorherigen App-Login stammen; Ablauf und Signatur des zuvor ausgestellten State werden geprüft. Gibt den geschützten JWT und gewählten Login-Kontext zurück. Kein gewöhnlicher Login mit E-Mail/Passwort; Query-Parameter ersetzen diesen JSON-Body nicht. - Operation-ID: get_v2_auth_access_token - Dokumentation: /docs/api/v2/operations/get-v2-auth-access-token-693552a44a18/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "code": "cd-synthetic-authorized-code", "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } EDOOBOX_JSON_EXAMPLE curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/access_token' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Fetch unterstützt GET mit Request Body nicht. // Verwende das cURL- oder Python-Beispiel oder einen HTTP-Client mit Unterstützung dieser Methode. throw new Error("Für GET mit Request Body ist kein gültiges Fetch-Beispiel verfügbar."); ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/access_token" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"code\":\"cd-synthetic-authorized-code\",\"token\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_access_token","summary":"App-Anmeldecode gegen Access-Token tauschen","description":"Liest code und den zugehörigen 50-Zeichen-token direkt aus einem JSON-Body auf oberster Ebene, auch bei GET. Der Code muss aus dem vorherigen App-Login stammen; Ablauf und Signatur des zuvor ausgestellten State werden geprüft. Gibt den geschützten JWT und gewählten Login-Kontext zurück. Kein gewöhnlicher Login mit E-Mail/Passwort; Query-Parameter ersetzen diesen JSON-Body nicht.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"access_token, edid und Unix-Ablaufzeitpunkt; access_token wird zusätzlich auf oberster Antwortebene ausgegeben.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"access_token":"synthetic-jwt-not-valid","edid":"edid_synthetic_login_context","expire":1790001800}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"access_token":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"edid":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"expire":{"type":"integer"}},"additionalProperties":true,"required":["access_token","edid","expire"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"token":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":50,"maxLength":50}},"additionalProperties":true,"required":["code","token"]},"examples":{"request":{"summary":"App-Anmeldecode gegen Access-Token tauschen","value":{"code":"cd-synthetic-authorized-code","token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}}}}},"required":true},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/access_token App-Anmeldecode gegen Access-Token tauschen Liest code und den zugehörigen 50-Zeichen-token direkt aus einem JSON-Body auf oberster Ebene, auch bei GET. Der Code muss aus dem vorherigen App-Login stammen; Ablauf und Signatur des zuvor ausgestellten State werden geprüft. Gibt den geschützten JWT und gewählten Login-Kontext zurück. Kein gewöhnlicher Login mit E-Mail/Passwort; Query-Parameter ersetzen diesen JSON-Body nicht. - Operation-ID: post_v2_auth_access_token - Dokumentation: /docs/api/v2/operations/post-v2-auth-access-token-c346cfd271c6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "code": "cd-synthetic-authorized-code", "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/access_token' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/access_token"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "code": "cd-synthetic-authorized-code", "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/access_token" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"code\":\"cd-synthetic-authorized-code\",\"token\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_access_token","summary":"App-Anmeldecode gegen Access-Token tauschen","description":"Liest code und den zugehörigen 50-Zeichen-token direkt aus einem JSON-Body auf oberster Ebene, auch bei GET. Der Code muss aus dem vorherigen App-Login stammen; Ablauf und Signatur des zuvor ausgestellten State werden geprüft. Gibt den geschützten JWT und gewählten Login-Kontext zurück. Kein gewöhnlicher Login mit E-Mail/Passwort; Query-Parameter ersetzen diesen JSON-Body nicht.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"access_token, edid und Unix-Ablaufzeitpunkt; access_token wird zusätzlich auf oberster Antwortebene ausgegeben.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"access_token":"synthetic-jwt-not-valid","edid":"edid_synthetic_login_context","expire":1790001800}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"access_token":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"edid":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"expire":{"type":"integer"}},"additionalProperties":true,"required":["access_token","edid","expire"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"token":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":50,"maxLength":50}},"additionalProperties":true,"required":["code","token"]},"examples":{"request":{"summary":"App-Anmeldecode gegen Access-Token tauschen","value":{"code":"cd-synthetic-authorized-code","token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/admin_invitation Gültigkeit einer Administrator-Einladung prüfen Prüft den vertraulichen Einladungs-code aus der Query und liefert Gültigkeit, Status, Ablaufzeit sowie Anzeigenamen. Fehlende/ungültige Codes liefern valid=false; die Einladung wird durch das Lesen nicht angenommen. - Operation-ID: get_v2_auth_admin_invitation - Dokumentation: /docs/api/v2/operations/get-v2-auth-admin-invitation-3d9e8ca18467/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | nein | Vertraulicher Einladungs-Code. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Einladungs-Code aus dem erhaltenen Link, 40 bis 200 Zeichen."},"description":"Vertraulicher Einladungs-Code."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/admin_invitation' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/admin_invitation"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/admin_invitation" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_admin_invitation","summary":"Gültigkeit einer Administrator-Einladung prüfen","description":"Prüft den vertraulichen Einladungs-code aus der Query und liefert Gültigkeit, Status, Ablaufzeit sowie Anzeigenamen. Fehlende/ungültige Codes liefern valid=false; die Einladung wird durch das Lesen nicht angenommen.","tags":["auth"],"security":[],"parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Einladungs-Code aus dem erhaltenen Link, 40 bis 200 Zeichen."},"description":"Vertraulicher Einladungs-Code."}],"responses":{"200":{"description":"Öffentliche Einladungsanzeige ohne interne IDs.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"valid":true,"status":"pending","expires_at":1790261000,"invited_by_name":"Kim Beispiel","admin_name":"Alex Beispiel"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"valid":{"type":"boolean"},"status":{"type":"string"},"expires_at":{"type":["integer","null"]},"invited_by_name":{"type":"string"},"admin_name":{"type":"string"}},"additionalProperties":true,"required":["valid","status","expires_at","invited_by_name","admin_name"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/admin_invitation/accept Administrator-Einladung annehmen Prüft den gültigen, noch nicht angenommenen Einladungscode, setzt das über die Passwortregeln geprüfte Passwort und markiert die Einladung als angenommen. Erstellt anschließend einen Administrator-Login; reply=token bleibt von der produktiven Tokenausgaberegel abhängig. code und password stehen im data-Objekt. - Operation-ID: post_v2_auth_admin_invitation_accept - Dokumentation: /docs/api/v2/operations/post-v2-auth-admin-invitation-accept-855634b85639/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "code": "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii", "password": "Synthetic-Invitation-42!", "reply": "cookie" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/admin_invitation/accept' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/admin_invitation/accept"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "code": "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii", "password": "Synthetic-Invitation-42!", "reply": "cookie" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/admin_invitation/accept" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"data\":{\"code\":\"iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\",\"password\":\"Synthetic-Invitation-42!\",\"reply\":\"cookie\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_admin_invitation_accept","summary":"Administrator-Einladung annehmen","description":"Prüft den gültigen, noch nicht angenommenen Einladungscode, setzt das über die Passwortregeln geprüfte Passwort und markiert die Einladung als angenommen. Erstellt anschließend einen Administrator-Login; reply=token bleibt von der produktiven Tokenausgaberegel abhängig. code und password stehen im data-Objekt.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"Grantabhängige Administrator-Login-Antwort mit Cookie bzw. erlaubtem Tokenmodus; kein neues Konto wird angelegt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"auth_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"code":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":40,"maxLength":200},"password":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Neues Passwort: mindestens acht Zeichen mit Klein-/Großbuchstaben, Ziffer und unterstütztem Sonderzeichen; Trivialpasswortliste wird geprüft. Der Server trimmt das Passwort.","minLength":8},"reply":{"type":"string","description":"token fordert Tokenmodus an; jeder andere Wert verwendet cookie. Produktionsregel kann Tokenmodus auf Cookie zurückführen."}},"additionalProperties":true,"required":["code","password"]}},"additionalProperties":true,"required":["data"]},"example":{"data":{"code":"iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii","password":"Synthetic-Invitation-42!","reply":"cookie"}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"code":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":40,"maxLength":200},"password":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Neues Passwort: mindestens acht Zeichen mit Klein-/Großbuchstaben, Ziffer und unterstütztem Sonderzeichen; Trivialpasswortliste wird geprüft. Der Server trimmt das Passwort.","minLength":8},"reply":{"type":"string","description":"token fordert Tokenmodus an; jeder andere Wert verwendet cookie. Produktionsregel kann Tokenmodus auf Cookie zurückführen."}},"additionalProperties":true,"required":["code","password"]},"example":{"code":"iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii","password":"Synthetic-Invitation-42!","reply":"cookie"}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/customer_endpoint/appwork_dates Termine für die kundenspezifische Anzeige lesen Liest die kundenspezifische Angebotsanzeige für den Zeitraum von vor 30 Minuten bis in acht Stunden. Query-code wird gegen den fest hinterlegten Legacy-Zugriffswert geprüft; dieser kundenspezifische Endpunkt erlaubt keine freie Kontowahl. POST verwendet dieselbe Query und keinen allgemeinen data-Envelope. - Operation-ID: get_v2_auth_customer_endpoint_appwork_dates - Dokumentation: /docs/api/v2/operations/get-v2-auth-customer-endpoint-appwork-dates-b0776183f6f1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | ja | Wird durch den kundenspezifischen Integrationsvertrag bereitgestellt. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":true,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Kundenspezifischer Legacy-Zugriffswert; nicht aus Beispielen ableiten."},"description":"Wird durch den kundenspezifischen Integrationsvertrag bereitgestellt."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_CODE:?QUERY_CODE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/customer_endpoint/appwork_dates' \ --url-query 'code='"${QUERY_CODE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/customer_endpoint/appwork_dates"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("code", required("QUERY_CODE")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/customer_endpoint/appwork_dates" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("code", os.environ["QUERY_CODE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_customer_endpoint_appwork_dates","summary":"Termine für die kundenspezifische Anzeige lesen","description":"Liest die kundenspezifische Angebotsanzeige für den Zeitraum von vor 30 Minuten bis in acht Stunden. Query-code wird gegen den fest hinterlegten Legacy-Zugriffswert geprüft; dieser kundenspezifische Endpunkt erlaubt keine freie Kontowahl. POST verwendet dieselbe Query und keinen allgemeinen data-Envelope.","tags":["auth"],"security":[],"parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Kundenspezifischer Legacy-Zugriffswert; nicht aus Beispielen ableiten."},"description":"Wird durch den kundenspezifischen Integrationsvertrag bereitgestellt."}],"responses":{"200":{"description":"Angebotsdatensätze der fest zugeordneten kundenspezifischen Anzeige.","content":{"application/json":{"example":{"success":true,"error":false,"data":[]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":["array","object"],"description":"Angebotsdaten aus dem konto-festen Offer-GET-Vertrag; standardmäßig über öffentliche Angebots-IDs indiziert, leer gegebenenfalls [].","additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/customer_endpoint/appwork_dates Termine für die kundenspezifische Anzeige lesen Liest die kundenspezifische Angebotsanzeige für den Zeitraum von vor 30 Minuten bis in acht Stunden. Query-code wird gegen den fest hinterlegten Legacy-Zugriffswert geprüft; dieser kundenspezifische Endpunkt erlaubt keine freie Kontowahl. POST verwendet dieselbe Query und keinen allgemeinen data-Envelope. - Operation-ID: post_v2_auth_customer_endpoint_appwork_dates - Dokumentation: /docs/api/v2/operations/post-v2-auth-customer-endpoint-appwork-dates-5653de862cd9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | ja | Wird durch den kundenspezifischen Integrationsvertrag bereitgestellt. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":true,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Kundenspezifischer Legacy-Zugriffswert; nicht aus Beispielen ableiten."},"description":"Wird durch den kundenspezifischen Integrationsvertrag bereitgestellt."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_CODE:?QUERY_CODE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/customer_endpoint/appwork_dates' \ --url-query 'code='"${QUERY_CODE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/customer_endpoint/appwork_dates"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("code", required("QUERY_CODE")); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/customer_endpoint/appwork_dates" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("code", os.environ["QUERY_CODE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_customer_endpoint_appwork_dates","summary":"Termine für die kundenspezifische Anzeige lesen","description":"Liest die kundenspezifische Angebotsanzeige für den Zeitraum von vor 30 Minuten bis in acht Stunden. Query-code wird gegen den fest hinterlegten Legacy-Zugriffswert geprüft; dieser kundenspezifische Endpunkt erlaubt keine freie Kontowahl. POST verwendet dieselbe Query und keinen allgemeinen data-Envelope.","tags":["auth"],"security":[],"parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Kundenspezifischer Legacy-Zugriffswert; nicht aus Beispielen ableiten."},"description":"Wird durch den kundenspezifischen Integrationsvertrag bereitgestellt."}],"responses":{"200":{"description":"Angebotsdatensätze der fest zugeordneten kundenspezifischen Anzeige.","content":{"application/json":{"example":{"success":true,"error":false,"data":[]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":["array","object"],"description":"Angebotsdaten aus dem konto-festen Offer-GET-Vertrag; standardmäßig über öffentliche Angebots-IDs indiziert, leer gegebenenfalls [].","additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/customer_endpoint/appwork_screen Kundenspezifische Bildschirmanzeige öffnen Liefert die vorhandene kundenspezifische HTML-Anzeige. Der Auth-Router erlaubt nur den fest definierten Funktionsnamen; der Aufruf selbst ändert keine Buchungen und benötigt keinen JSON-Payload. - Operation-ID: get_v2_auth_customer_endpoint_appwork_screen - Dokumentation: /docs/api/v2/operations/get-v2-auth-customer-endpoint-appwork-screen-446cf6243121/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/customer_endpoint/appwork_screen' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/customer_endpoint/appwork_screen"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/customer_endpoint/appwork_screen" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_customer_endpoint_appwork_screen","summary":"Kundenspezifische Bildschirmanzeige öffnen","description":"Liefert die vorhandene kundenspezifische HTML-Anzeige. Der Auth-Router erlaubt nur den fest definierten Funktionsnamen; der Aufruf selbst ändert keine Buchungen und benötigt keinen JSON-Payload.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"HTML-Bildschirmanzeige.","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/customer_endpoint/appwork_screen Kundenspezifische Bildschirmanzeige öffnen Liefert die vorhandene kundenspezifische HTML-Anzeige. Der Auth-Router erlaubt nur den fest definierten Funktionsnamen; der Aufruf selbst ändert keine Buchungen und benötigt keinen JSON-Payload. - Operation-ID: post_v2_auth_customer_endpoint_appwork_screen - Dokumentation: /docs/api/v2/operations/post-v2-auth-customer-endpoint-appwork-screen-33bba12f8f51/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/customer_endpoint/appwork_screen' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/customer_endpoint/appwork_screen"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/customer_endpoint/appwork_screen" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_customer_endpoint_appwork_screen","summary":"Kundenspezifische Bildschirmanzeige öffnen","description":"Liefert die vorhandene kundenspezifische HTML-Anzeige. Der Auth-Router erlaubt nur den fest definierten Funktionsnamen; der Aufruf selbst ändert keine Buchungen und benötigt keinen JSON-Payload.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"HTML-Bildschirmanzeige.","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/customer_endpoint/SpracheCB_screen Kundenspezifische Demoanzeige öffnen Liefert die vorhandene kundenspezifische HTML-Anzeige. Der Auth-Router erlaubt nur den fest definierten Funktionsnamen; der Aufruf selbst ändert keine Buchungen und benötigt keinen JSON-Payload. - Operation-ID: get_v2_auth_customer_endpoint_SpracheCB_screen - Dokumentation: /docs/api/v2/operations/get-v2-auth-customer-endpoint-sprachecb-screen-b16b9df141ab/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/customer_endpoint/SpracheCB_screen' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/customer_endpoint/SpracheCB_screen"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/customer_endpoint/SpracheCB_screen" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_customer_endpoint_SpracheCB_screen","summary":"Kundenspezifische Demoanzeige öffnen","description":"Liefert die vorhandene kundenspezifische HTML-Anzeige. Der Auth-Router erlaubt nur den fest definierten Funktionsnamen; der Aufruf selbst ändert keine Buchungen und benötigt keinen JSON-Payload.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"HTML-Bildschirmanzeige.","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/customer_endpoint/SpracheCB_screen Kundenspezifische Demoanzeige öffnen Liefert die vorhandene kundenspezifische HTML-Anzeige. Der Auth-Router erlaubt nur den fest definierten Funktionsnamen; der Aufruf selbst ändert keine Buchungen und benötigt keinen JSON-Payload. - Operation-ID: post_v2_auth_customer_endpoint_SpracheCB_screen - Dokumentation: /docs/api/v2/operations/post-v2-auth-customer-endpoint-sprachecb-screen-fc3dc46282e9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/customer_endpoint/SpracheCB_screen' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/customer_endpoint/SpracheCB_screen"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/customer_endpoint/SpracheCB_screen" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_customer_endpoint_SpracheCB_screen","summary":"Kundenspezifische Demoanzeige öffnen","description":"Liefert die vorhandene kundenspezifische HTML-Anzeige. Der Auth-Router erlaubt nur den fest definierten Funktionsnamen; der Aufruf selbst ändert keine Buchungen und benötigt keinen JSON-Payload.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"HTML-Bildschirmanzeige.","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/DEMO_LOGIN Beispielansicht für den App-Login anzeigen Zeigt die HTML-Beispielansicht für die App-Übergabe. state und URL-codierte redirect_uri werden als Query gelesen; die Ansicht selbst meldet niemanden an und erstellt keinen Token. - Operation-ID: get_v2_auth_DEMO_LOGIN - Dokumentation: /docs/api/v2/operations/get-v2-auth-demo-login-377c1d527f6f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | state | query | nein | Signierter Übergabe-State aus authorization_code. | | redirect_uri | query | nein | URL-codierte Rücksprungadresse der App. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"state","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Signierter Übergabe-State aus authorization_code."},{"name":"redirect_uri","in":"query","required":false,"schema":{"type":"string"},"description":"URL-codierte Rücksprungadresse der App."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/DEMO_LOGIN' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/DEMO_LOGIN"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/DEMO_LOGIN" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_DEMO_LOGIN","summary":"Beispielansicht für den App-Login anzeigen","description":"Zeigt die HTML-Beispielansicht für die App-Übergabe. state und URL-codierte redirect_uri werden als Query gelesen; die Ansicht selbst meldet niemanden an und erstellt keinen Token.","tags":["auth"],"security":[],"parameters":[{"name":"state","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Signierter Übergabe-State aus authorization_code."},{"name":"redirect_uri","in":"query","required":false,"schema":{"type":"string"},"description":"URL-codierte Rücksprungadresse der App."}],"responses":{"200":{"description":"HTML-Login-Beispielansicht.","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/DEMO_LOGIN Beispielansicht für den App-Login anzeigen Zeigt die HTML-Beispielansicht für die App-Übergabe. state und URL-codierte redirect_uri werden als Query gelesen; die Ansicht selbst meldet niemanden an und erstellt keinen Token. - Operation-ID: post_v2_auth_DEMO_LOGIN - Dokumentation: /docs/api/v2/operations/post-v2-auth-demo-login-98b3971176a1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | state | query | nein | Signierter Übergabe-State aus authorization_code. | | redirect_uri | query | nein | URL-codierte Rücksprungadresse der App. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"state","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Signierter Übergabe-State aus authorization_code."},{"name":"redirect_uri","in":"query","required":false,"schema":{"type":"string"},"description":"URL-codierte Rücksprungadresse der App."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/DEMO_LOGIN' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/DEMO_LOGIN"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/DEMO_LOGIN" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_DEMO_LOGIN","summary":"Beispielansicht für den App-Login anzeigen","description":"Zeigt die HTML-Beispielansicht für die App-Übergabe. state und URL-codierte redirect_uri werden als Query gelesen; die Ansicht selbst meldet niemanden an und erstellt keinen Token.","tags":["auth"],"security":[],"parameters":[{"name":"state","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Signierter Übergabe-State aus authorization_code."},{"name":"redirect_uri","in":"query","required":false,"schema":{"type":"string"},"description":"URL-codierte Rücksprungadresse der App."}],"responses":{"200":{"description":"HTML-Login-Beispielansicht.","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/endpoint/date_calendar/{token} Freigegebenen Widget-Kalender lesen Entschlüsselt die serverseitig erzeugte Freigabe im token-Pfadsegment und liest den darin festgelegten Widget-Kalender. start/end begrenzen die Anzeige; Query ist für GET und POST möglich, alternativ POST-Formfelder. token ist opakes geschütztes Freigabematerial, keine Datenbank-ID. Eine freie Änderung des Kontos im Payload wird nicht angeboten. - Operation-ID: get_v2_auth_endpoint_date_calendar_token - Dokumentation: /docs/api/v2/operations/get-v2-auth-endpoint-date-calendar-token-d68e236eadfc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | token | path | ja | Serverseitig erzeugte Kalender-Freigabe. | | start | query | nein | Beginn des angeforderten Zeitraums, durch strtotime gelesen. | | end | query | nein | Ende des angeforderten Zeitraums, durch strtotime gelesen. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"token","in":"path","required":true,"description":"Serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"start","in":"query","required":false,"schema":{"type":"string"},"description":"Beginn des angeforderten Zeitraums, durch strtotime gelesen."},{"name":"end","in":"query","required":false,"schema":{"type":"string"},"description":"Ende des angeforderten Zeitraums, durch strtotime gelesen."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_TOKEN:?PATH_TOKEN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/endpoint/date_calendar/'"${PATH_TOKEN}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/endpoint/date_calendar/{token}"; path = path.replaceAll("{token}", encodeURIComponent(required("PATH_TOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/endpoint/date_calendar/{token}" path = path.replace("{token}", urllib.parse.quote(os.environ["PATH_TOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_endpoint_date_calendar_token","summary":"Freigegebenen Widget-Kalender lesen","description":"Entschlüsselt die serverseitig erzeugte Freigabe im token-Pfadsegment und liest den darin festgelegten Widget-Kalender. start/end begrenzen die Anzeige; Query ist für GET und POST möglich, alternativ POST-Formfelder. token ist opakes geschütztes Freigabematerial, keine Datenbank-ID. Eine freie Änderung des Kontos im Payload wird nicht angeboten.","tags":["auth"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"Serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"start","in":"query","required":false,"schema":{"type":"string"},"description":"Beginn des angeforderten Zeitraums, durch strtotime gelesen."},{"name":"end","in":"query","required":false,"schema":{"type":"string"},"description":"Ende des angeforderten Zeitraums, durch strtotime gelesen."}],"responses":{"200":{"description":"Kundenspezifisches Kalender-JSON mit ok, rsl und offers auf oberster Ebene; kein generischer data-Listenvertrag.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"rsl":{"type":["array","object"]},"offers":{"type":["array","object"]}},"additionalProperties":true},"example":{"ok":false,"rsl":[],"offers":[]}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/endpoint/date_calendar/{token} Freigegebenen Widget-Kalender lesen Entschlüsselt die serverseitig erzeugte Freigabe im token-Pfadsegment und liest den darin festgelegten Widget-Kalender. start/end begrenzen die Anzeige; Query ist für GET und POST möglich, alternativ POST-Formfelder. token ist opakes geschütztes Freigabematerial, keine Datenbank-ID. Eine freie Änderung des Kontos im Payload wird nicht angeboten. - Operation-ID: post_v2_auth_endpoint_date_calendar_token - Dokumentation: /docs/api/v2/operations/post-v2-auth-endpoint-date-calendar-token-72137829794a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | token | path | ja | Serverseitig erzeugte Kalender-Freigabe. | | start | query | nein | Beginn des angeforderten Zeitraums, durch strtotime gelesen. | | end | query | nein | Ende des angeforderten Zeitraums, durch strtotime gelesen. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"token","in":"path","required":true,"description":"Serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"start","in":"query","required":false,"schema":{"type":"string"},"description":"Beginn des angeforderten Zeitraums, durch strtotime gelesen."},{"name":"end","in":"query","required":false,"schema":{"type":"string"},"description":"Ende des angeforderten Zeitraums, durch strtotime gelesen."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_TOKEN:?PATH_TOKEN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/endpoint/date_calendar/'"${PATH_TOKEN}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/endpoint/date_calendar/{token}"; path = path.replaceAll("{token}", encodeURIComponent(required("PATH_TOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/endpoint/date_calendar/{token}" path = path.replace("{token}", urllib.parse.quote(os.environ["PATH_TOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_endpoint_date_calendar_token","summary":"Freigegebenen Widget-Kalender lesen","description":"Entschlüsselt die serverseitig erzeugte Freigabe im token-Pfadsegment und liest den darin festgelegten Widget-Kalender. start/end begrenzen die Anzeige; Query ist für GET und POST möglich, alternativ POST-Formfelder. token ist opakes geschütztes Freigabematerial, keine Datenbank-ID. Eine freie Änderung des Kontos im Payload wird nicht angeboten.","tags":["auth"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"Serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"start","in":"query","required":false,"schema":{"type":"string"},"description":"Beginn des angeforderten Zeitraums, durch strtotime gelesen."},{"name":"end","in":"query","required":false,"schema":{"type":"string"},"description":"Ende des angeforderten Zeitraums, durch strtotime gelesen."}],"responses":{"200":{"description":"Kundenspezifisches Kalender-JSON mit ok, rsl und offers auf oberster Ebene; kein generischer data-Listenvertrag.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"rsl":{"type":["array","object"]},"offers":{"type":["array","object"]}},"additionalProperties":true},"example":{"ok":false,"rsl":[],"offers":[]}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/forgot_password/change_request Passwort-Rücksetzlink prüfen Prüft Query-code auf Format, noch gültige Frist und unverbrauchten Status. Schreibt kein neues Passwort und verbraucht den Code nicht; technische Zugriffslimits gelten. - Operation-ID: get_v2_auth_forgot_password_change_request - Dokumentation: /docs/api/v2/operations/get-v2-auth-forgot-password-change-request-0a07c73d06f9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | ja | Code aus dem Rücksetzlink. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":true,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":120,"maxLength":150},"description":"Code aus dem Rücksetzlink."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_CODE:?QUERY_CODE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/forgot_password/change_request' \ --url-query 'code='"${QUERY_CODE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/forgot_password/change_request"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("code", required("QUERY_CODE")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/forgot_password/change_request" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("code", os.environ["QUERY_CODE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_forgot_password_change_request","summary":"Passwort-Rücksetzlink prüfen","description":"Prüft Query-code auf Format, noch gültige Frist und unverbrauchten Status. Schreibt kein neues Passwort und verbraucht den Code nicht; technische Zugriffslimits gelten.","tags":["auth"],"security":[],"parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":120,"maxLength":150},"description":"Code aus dem Rücksetzlink."}],"responses":{"200":{"description":"valid zeigt, ob der Link noch verwendbar ist.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"valid":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"valid":{"type":"boolean"}},"additionalProperties":true,"required":["valid"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/forgot_password/change_request Neues Passwort über Rücksetzlink speichern Prüft den unverbrauchten data.code und das neue data.password gegen die Passwortregeln. Speichert den Passwort-Hash des zugehörigen Administrators und verbraucht den Rücksetzcode. Es erfolgt kein automatischer Login. - Operation-ID: post_v2_auth_forgot_password_change_request - Dokumentation: /docs/api/v2/operations/post-v2-auth-forgot-password-change-request-b4b002069949/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "code": "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "password": "Synthetic-Reset-Password-42!" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/forgot_password/change_request' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/forgot_password/change_request"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "code": "rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr", "password": "Synthetic-Reset-Password-42!" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/forgot_password/change_request" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"data\":{\"code\":\"rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr\",\"password\":\"Synthetic-Reset-Password-42!\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_forgot_password_change_request","summary":"Neues Passwort über Rücksetzlink speichern","description":"Prüft den unverbrauchten data.code und das neue data.password gegen die Passwortregeln. Speichert den Passwort-Hash des zugehörigen Administrators und verbraucht den Rücksetzcode. Es erfolgt kein automatischer Login.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"valid=true und message=password_changed nach erfolgreicher Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"valid":true,"message":"password_changed"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"valid":{"type":"boolean"},"message":{"const":"password_changed"}},"additionalProperties":true,"required":["valid","message"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"code":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":100,"maxLength":150},"password":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Neues Passwort: mindestens acht Zeichen mit Klein-/Großbuchstaben, Ziffer und unterstütztem Sonderzeichen; Trivialpasswortliste wird geprüft. Der Server trimmt das Passwort.","minLength":8}},"additionalProperties":true,"required":["code","password"]}},"additionalProperties":true,"required":["data"]},"example":{"data":{"code":"rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr","password":"Synthetic-Reset-Password-42!"}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"code":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":100,"maxLength":150},"password":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Neues Passwort: mindestens acht Zeichen mit Klein-/Großbuchstaben, Ziffer und unterstütztem Sonderzeichen; Trivialpasswortliste wird geprüft. Der Server trimmt das Passwort.","minLength":8}},"additionalProperties":true,"required":["code","password"]},"example":{"code":"rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr","password":"Synthetic-Reset-Password-42!"}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/forgot_password/send_request Passwort-Zurücksetzung anfordern Prüft data.email, data.name (Vorname, mindestens zwei Zeichen) und reCAPTCHA und legt bei passendem aktivem, nicht per SAML gebundenem Administrator einen zeitlich begrenzten Rücksetzlink in den E-Mail-Ausgang. Auch der GET-Alias liest JSON-Body bzw. POST-Form statt Query und kann Mail auslösen. Die Antwort verrät nicht, ob der Administrator existiert. - Operation-ID: get_v2_auth_forgot_password_send_request - Dokumentation: /docs/api/v2/operations/get-v2-auth-forgot-password-send-request-7b7dcfb619b7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "email": "alex@example.invalid", "name": "Alex", "g-recaptcha-response": "synthetic-recaptcha-proof" } } EDOOBOX_JSON_EXAMPLE curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/forgot_password/send_request' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Fetch unterstützt GET mit Request Body nicht. // Verwende das cURL- oder Python-Beispiel oder einen HTTP-Client mit Unterstützung dieser Methode. throw new Error("Für GET mit Request Body ist kein gültiges Fetch-Beispiel verfügbar."); ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/forgot_password/send_request" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"data\":{\"email\":\"alex@example.invalid\",\"name\":\"Alex\",\"g-recaptcha-response\":\"synthetic-recaptcha-proof\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_forgot_password_send_request","summary":"Passwort-Zurücksetzung anfordern","description":"Prüft data.email, data.name (Vorname, mindestens zwei Zeichen) und reCAPTCHA und legt bei passendem aktivem, nicht per SAML gebundenem Administrator einen zeitlich begrenzten Rücksetzlink in den E-Mail-Ausgang. Auch der GET-Alias liest JSON-Body bzw. POST-Form statt Query und kann Mail auslösen. Die Antwort verrät nicht, ob der Administrator existiert.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"Neutrale Versandbestätigung ohne Existenz-Auskunft.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"message":"mail_sent_if_email_and_name_exist"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"message":{"const":"mail_sent_if_email_and_name_exist"}},"additionalProperties":true,"required":["message"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"email":{"type":"string","format":"email"},"name":{"type":"string","minLength":2},"g-recaptcha-response":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":8}},"additionalProperties":true,"required":["email","name","g-recaptcha-response"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Passwort-Zurücksetzung anfordern","value":{"data":{"email":"alex@example.invalid","name":"Alex","g-recaptcha-response":"synthetic-recaptcha-proof"}}}}}},"required":true},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/forgot_password/send_request Passwort-Zurücksetzung anfordern Prüft data.email, data.name (Vorname, mindestens zwei Zeichen) und reCAPTCHA und legt bei passendem aktivem, nicht per SAML gebundenem Administrator einen zeitlich begrenzten Rücksetzlink in den E-Mail-Ausgang. Auch der GET-Alias liest JSON-Body bzw. POST-Form statt Query und kann Mail auslösen. Die Antwort verrät nicht, ob der Administrator existiert. - Operation-ID: post_v2_auth_forgot_password_send_request - Dokumentation: /docs/api/v2/operations/post-v2-auth-forgot-password-send-request-4e22b58b845c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "email": "alex@example.invalid", "name": "Alex", "g-recaptcha-response": "synthetic-recaptcha-proof" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/forgot_password/send_request' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/forgot_password/send_request"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "email": "alex@example.invalid", "name": "Alex", "g-recaptcha-response": "synthetic-recaptcha-proof" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/forgot_password/send_request" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"data\":{\"email\":\"alex@example.invalid\",\"name\":\"Alex\",\"g-recaptcha-response\":\"synthetic-recaptcha-proof\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_forgot_password_send_request","summary":"Passwort-Zurücksetzung anfordern","description":"Prüft data.email, data.name (Vorname, mindestens zwei Zeichen) und reCAPTCHA und legt bei passendem aktivem, nicht per SAML gebundenem Administrator einen zeitlich begrenzten Rücksetzlink in den E-Mail-Ausgang. Auch der GET-Alias liest JSON-Body bzw. POST-Form statt Query und kann Mail auslösen. Die Antwort verrät nicht, ob der Administrator existiert.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"Neutrale Versandbestätigung ohne Existenz-Auskunft.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"message":"mail_sent_if_email_and_name_exist"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"message":{"const":"mail_sent_if_email_and_name_exist"}},"additionalProperties":true,"required":["message"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"email":{"type":"string","format":"email"},"name":{"type":"string","minLength":2},"g-recaptcha-response":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":8}},"additionalProperties":true,"required":["email","name","g-recaptcha-response"]}},"additionalProperties":true,"required":["data"]},"example":{"data":{"email":"alex@example.invalid","name":"Alex","g-recaptcha-response":"synthetic-recaptcha-proof"}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"name":{"type":"string","minLength":2},"g-recaptcha-response":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben.","minLength":8}},"additionalProperties":true,"required":["email","name","g-recaptcha-response"]},"example":{"email":"alex@example.invalid","name":"Alex","g-recaptcha-response":"synthetic-recaptcha-proof"}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/i/{token} Freigegebenen iCalendar-Feed abrufen Liefert den durch den verschlüsselten Pfad-token freigegebenen Kalender als text/calendar. Der Query-Parameter t kann den Pfad-token ersetzen; n beeinflusst den Kalendernamen. Die Freigabe bestimmt Konto, Kalender und Zeitzone. GET und POST lesen denselben Feed, ein JSON-data-Payload wird nicht verwendet; der Handler kann den Feed im technischen Cache erzeugen. - Operation-ID: get_v2_auth_i_token - Dokumentation: /docs/api/v2/operations/get-v2-auth-i-token-fd7107030e79/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | token | path | ja | Verschlüsselte serverseitig erzeugte Kalender-Freigabe. | | t | query | nein | Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad. | | n | query | nein | Anzeigename des Kalenders. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"token","in":"path","required":true,"description":"Verschlüsselte serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"t","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad."},{"name":"n","in":"query","required":false,"schema":{"type":"string"},"description":"Anzeigename des Kalenders."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_TOKEN:?PATH_TOKEN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/i/'"${PATH_TOKEN}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/i/{token}"; path = path.replaceAll("{token}", encodeURIComponent(required("PATH_TOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/i/{token}" path = path.replace("{token}", urllib.parse.quote(os.environ["PATH_TOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_i_token","summary":"Freigegebenen iCalendar-Feed abrufen","description":"Liefert den durch den verschlüsselten Pfad-token freigegebenen Kalender als text/calendar. Der Query-Parameter t kann den Pfad-token ersetzen; n beeinflusst den Kalendernamen. Die Freigabe bestimmt Konto, Kalender und Zeitzone. GET und POST lesen denselben Feed, ein JSON-data-Payload wird nicht verwendet; der Handler kann den Feed im technischen Cache erzeugen.","tags":["auth"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"Verschlüsselte serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"t","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad."},{"name":"n","in":"query","required":false,"schema":{"type":"string"},"description":"Anzeigename des Kalenders."}],"responses":{"200":{"description":"iCalendar-Text; ungültige Freigaben liefern den normalen JSON-Fehlervertrag.","content":{"text/calendar":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/i/{token} Freigegebenen iCalendar-Feed abrufen Liefert den durch den verschlüsselten Pfad-token freigegebenen Kalender als text/calendar. Der Query-Parameter t kann den Pfad-token ersetzen; n beeinflusst den Kalendernamen. Die Freigabe bestimmt Konto, Kalender und Zeitzone. GET und POST lesen denselben Feed, ein JSON-data-Payload wird nicht verwendet; der Handler kann den Feed im technischen Cache erzeugen. - Operation-ID: post_v2_auth_i_token - Dokumentation: /docs/api/v2/operations/post-v2-auth-i-token-a82364f7c7d2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | token | path | ja | Verschlüsselte serverseitig erzeugte Kalender-Freigabe. | | t | query | nein | Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad. | | n | query | nein | Anzeigename des Kalenders. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"token","in":"path","required":true,"description":"Verschlüsselte serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"t","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad."},{"name":"n","in":"query","required":false,"schema":{"type":"string"},"description":"Anzeigename des Kalenders."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_TOKEN:?PATH_TOKEN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/i/'"${PATH_TOKEN}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/i/{token}"; path = path.replaceAll("{token}", encodeURIComponent(required("PATH_TOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/i/{token}" path = path.replace("{token}", urllib.parse.quote(os.environ["PATH_TOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_i_token","summary":"Freigegebenen iCalendar-Feed abrufen","description":"Liefert den durch den verschlüsselten Pfad-token freigegebenen Kalender als text/calendar. Der Query-Parameter t kann den Pfad-token ersetzen; n beeinflusst den Kalendernamen. Die Freigabe bestimmt Konto, Kalender und Zeitzone. GET und POST lesen denselben Feed, ein JSON-data-Payload wird nicht verwendet; der Handler kann den Feed im technischen Cache erzeugen.","tags":["auth"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"Verschlüsselte serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"t","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad."},{"name":"n","in":"query","required":false,"schema":{"type":"string"},"description":"Anzeigename des Kalenders."}],"responses":{"200":{"description":"iCalendar-Text; ungültige Freigaben liefern den normalen JSON-Fehlervertrag.","content":{"text/calendar":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/ical/{token} Freigegebenen iCalendar-Feed abrufen Liefert den durch den verschlüsselten Pfad-token freigegebenen Kalender als text/calendar. Der Query-Parameter t kann den Pfad-token ersetzen; n beeinflusst den Kalendernamen. Die Freigabe bestimmt Konto, Kalender und Zeitzone. GET und POST lesen denselben Feed, ein JSON-data-Payload wird nicht verwendet; der Handler kann den Feed im technischen Cache erzeugen. - Operation-ID: get_v2_auth_ical_token - Dokumentation: /docs/api/v2/operations/get-v2-auth-ical-token-120416f5337e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | token | path | ja | Verschlüsselte serverseitig erzeugte Kalender-Freigabe. | | t | query | nein | Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad. | | n | query | nein | Anzeigename des Kalenders. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"token","in":"path","required":true,"description":"Verschlüsselte serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"t","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad."},{"name":"n","in":"query","required":false,"schema":{"type":"string"},"description":"Anzeigename des Kalenders."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_TOKEN:?PATH_TOKEN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/ical/'"${PATH_TOKEN}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/ical/{token}"; path = path.replaceAll("{token}", encodeURIComponent(required("PATH_TOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/ical/{token}" path = path.replace("{token}", urllib.parse.quote(os.environ["PATH_TOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_ical_token","summary":"Freigegebenen iCalendar-Feed abrufen","description":"Liefert den durch den verschlüsselten Pfad-token freigegebenen Kalender als text/calendar. Der Query-Parameter t kann den Pfad-token ersetzen; n beeinflusst den Kalendernamen. Die Freigabe bestimmt Konto, Kalender und Zeitzone. GET und POST lesen denselben Feed, ein JSON-data-Payload wird nicht verwendet; der Handler kann den Feed im technischen Cache erzeugen.","tags":["auth"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"Verschlüsselte serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"t","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad."},{"name":"n","in":"query","required":false,"schema":{"type":"string"},"description":"Anzeigename des Kalenders."}],"responses":{"200":{"description":"iCalendar-Text; ungültige Freigaben liefern den normalen JSON-Fehlervertrag.","content":{"text/calendar":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/ical/{token} Freigegebenen iCalendar-Feed abrufen Liefert den durch den verschlüsselten Pfad-token freigegebenen Kalender als text/calendar. Der Query-Parameter t kann den Pfad-token ersetzen; n beeinflusst den Kalendernamen. Die Freigabe bestimmt Konto, Kalender und Zeitzone. GET und POST lesen denselben Feed, ein JSON-data-Payload wird nicht verwendet; der Handler kann den Feed im technischen Cache erzeugen. - Operation-ID: post_v2_auth_ical_token - Dokumentation: /docs/api/v2/operations/post-v2-auth-ical-token-5f779e87466c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | token | path | ja | Verschlüsselte serverseitig erzeugte Kalender-Freigabe. | | t | query | nein | Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad. | | n | query | nein | Anzeigename des Kalenders. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"token","in":"path","required":true,"description":"Verschlüsselte serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"t","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad."},{"name":"n","in":"query","required":false,"schema":{"type":"string"},"description":"Anzeigename des Kalenders."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_TOKEN:?PATH_TOKEN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/ical/'"${PATH_TOKEN}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/ical/{token}"; path = path.replaceAll("{token}", encodeURIComponent(required("PATH_TOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/ical/{token}" path = path.replace("{token}", urllib.parse.quote(os.environ["PATH_TOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_ical_token","summary":"Freigegebenen iCalendar-Feed abrufen","description":"Liefert den durch den verschlüsselten Pfad-token freigegebenen Kalender als text/calendar. Der Query-Parameter t kann den Pfad-token ersetzen; n beeinflusst den Kalendernamen. Die Freigabe bestimmt Konto, Kalender und Zeitzone. GET und POST lesen denselben Feed, ein JSON-data-Payload wird nicht verwendet; der Handler kann den Feed im technischen Cache erzeugen.","tags":["auth"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"Verschlüsselte serverseitig erzeugte Kalender-Freigabe.","schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."}},{"name":"t","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Optionale alternative Kalender-Freigabe; hat Vorrang vor dem Pfad."},{"name":"n","in":"query","required":false,"schema":{"type":"string"},"description":"Anzeigename des Kalenders."}],"responses":{"200":{"description":"iCalendar-Text; ungültige Freigaben liefern den normalen JSON-Fehlervertrag.","content":{"text/calendar":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/list Login-Sitzungen prüfen Liest Login-Sitzungen und öffentliche Metadaten im gewählten Konto. grant-type=access_token muss für diese GET-auth-Route ausdrücklich gesetzt sein; Bearer-Token bzw. Sitzungscookie und edid bleiben erforderlich. auth-Leserechte werden zusätzlich geprüft. Die id einer Sitzung ist der opake edid-Kontext, keine v2-Objekt-ID. - Operation-ID: get_v2_auth_list - Dokumentation: /docs/api/v2/operations/get-v2-auth-list-801e13cdf180/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | grant-type | header | ja | Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"grant-type","in":"header","required":true,"schema":{"type":"string","const":"access_token"},"description":"Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${HEADER_GRANT_TYPE:?HEADER_GRANT_TYPE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'grant-type: '"${HEADER_GRANT_TYPE}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers["grant-type"] = "" + required("HEADER_GRANT_TYPE"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["grant-type"] = "" + os.environ["HEADER_GRANT_TYPE"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_list","summary":"Login-Sitzungen prüfen","description":"Liest Login-Sitzungen und öffentliche Metadaten im gewählten Konto. grant-type=access_token muss für diese GET-auth-Route ausdrücklich gesetzt sein; Bearer-Token bzw. Sitzungscookie und edid bleiben erforderlich. auth-Leserechte werden zusätzlich geprüft. Die id einer Sitzung ist der opake edid-Kontext, keine v2-Objekt-ID.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"grant-type","in":"header","required":true,"schema":{"type":"string","const":"access_token"},"description":"Wählt die authentifizierte Ressource statt des öffentlichen Auth-Routers."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edid_synthetic_login_context":{"id":"edid_synthetic_login_context"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort; keine Datenbank-ID."},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"ip_address":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45,"type":"string"},"create_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":55,"anyOf":[{"type":"string"},{"const":false}]},"expiry_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"anyOf":[{"type":"string"},{"const":false}]},"logout_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":75,"anyOf":[{"type":"string"},{"const":false}]},"remember":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"},"2fa_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":96,"type":"boolean"},"2fa":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["2fa_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"blocked":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":120,"anyOf":[{"type":"string"},{"const":false}]},"log_data":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":139}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"getAuth","line":278},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/list/2fa Zweiten Faktor für die Anmeldung bestätigen Bei einem noch eingeschränkten Login-Kontext prüft die Route den zweiten Faktor und erneuert bei Erfolg das Login. type=otpauth/backupkey benötigt code, yubikey benötigt yubikey, phone sendet mit auth2fa+delivery einen Code und prüft ihn anschließend mit review+code. Passkey verwendet action=options und danach action=verify mit ceremony+credential; browsergebundene Challenge und User-Verifikation werden geprüft. Für bereits vollständig authentifizierte Logins bestätigt die Modellroute lediglich {id,2fa:true}. Ein /list-Alias benötigt bei normaler Modellverarbeitung id; für den vorgeschalteten 2FA-Handshake ist der Header-edid maßgeblich. - Operation-ID: put_v2_auth_list_2fa - Dokumentation: /docs/api/v2/operations/put-v2-auth-list-2fa-707d00e70636/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "type": "otpauth", "code": "123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/list/2fa' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/list/2fa"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "type": "otpauth", "code": "123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/list/2fa" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"type\":\"otpauth\",\"code\":\"123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_list_2fa","summary":"Zweiten Faktor für die Anmeldung bestätigen","description":"Bei einem noch eingeschränkten Login-Kontext prüft die Route den zweiten Faktor und erneuert bei Erfolg das Login. type=otpauth/backupkey benötigt code, yubikey benötigt yubikey, phone sendet mit auth2fa+delivery einen Code und prüft ihn anschließend mit review+code. Passkey verwendet action=options und danach action=verify mit ceremony+credential; browsergebundene Challenge und User-Verifikation werden geprüft. Für bereits vollständig authentifizierte Logins bestätigt die Modellroute lediglich {id,2fa:true}. Ein /list-Alias benötigt bei normaler Modellverarbeitung id; für den vorgeschalteten 2FA-Handshake ist der Header-edid maßgeblich.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Bei bestehender Authentifizierung {id,2fa:true}; im 2FA-Handshake Challenge-/Review-Daten oder erneuerte Login-Antwort. Geschütztes Material nicht protokollieren.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"edid_synthetic_login_context","2fa":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"2fa":{"type":"boolean"}},"additionalProperties":true},{"type":"object","properties":{"ceremony":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"options":{"type":"object","additionalProperties":true}},"additionalProperties":true},{"type":"object","properties":{"review":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"valid":{"type":"string"}},"additionalProperties":true},{"type":"object","additionalProperties":true}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"enum":["otpauth","backupkey","yubikey","phone","passkey"]},"code":{"type":["string","integer"],"writeOnly":true,"x-edoobox-sensitive":true},"yubikey":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"auth2fa":{"type":"string","pattern":"^auth2fa_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"delivery":{"type":"string","description":"call wählt Sprachanruf; alle anderen Werte werden als sms behandelt."},"review":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"action":{"enum":["options","verify"]},"ceremony":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"credential":{"type":"object","additionalProperties":true,"x-edoobox-sensitive":true,"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]+$","x-edoobox-id-kind":"webauthn-credential","description":"Base64URL-codierte Authenticator-Credential-Bytes; keine edoobox-Objekt- oder Datenbank-ID.","examples":["c3ludGhldGljLWNyZWRlbnRpYWw"]},"rawId":{"type":"string"},"type":{"type":"string"},"response":{"type":"object","additionalProperties":true},"clientExtensionResults":{"type":"object","additionalProperties":true}},"writeOnly":true},"reply":{"type":"string"}},"additionalProperties":true,"anyOf":[{"type":"object","properties":{"type":{"const":"otpauth"}},"additionalProperties":true,"required":["type","code"]},{"type":"object","properties":{"type":{"const":"backupkey"}},"additionalProperties":true,"required":["type","code"]},{"type":"object","properties":{"type":{"const":"yubikey"}},"additionalProperties":true,"required":["type","yubikey"]},{"type":"object","properties":{"type":{"const":"phone"}},"additionalProperties":true,"required":["type","auth2fa","delivery"]},{"type":"object","properties":{"type":{"const":"phone"}},"additionalProperties":true,"required":["type","review","code"]},{"type":"object","properties":{"type":{"const":"passkey"},"action":{"const":"options"}},"additionalProperties":true,"required":["type","action"]},{"type":"object","properties":{"type":{"const":"passkey"},"action":{"const":"verify"}},"additionalProperties":true,"required":["type","action","ceremony","credential"]}]},"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zweiten Faktor für die Anmeldung bestätigen","value":{"data":{"type":"otpauth","code":"123456"}}}}}}},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":227}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/list/logout Alle Login-Kontexte abmelden Meldet alle noch gültigen Login-Kontexte des aktuellen Administrator-Cookies ab, sperrt ihre edid-Werte und löscht das Login-Cookie. Falls kein Multi-Login-Cookie vorliegt, wird der gewählte Kontext abgemeldet. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen. - Operation-ID: put_v2_auth_list_logout - Dokumentation: /docs/api/v2/operations/put-v2-auth-list-logout-1cfd1957e9cc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "edid_synthetic_login_context", "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/list/logout' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/list/logout"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "edid_synthetic_login_context", "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/list/logout" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"edid_synthetic_login_context\",\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_list_logout","summary":"Alle Login-Kontexte abmelden","description":"Meldet alle noch gültigen Login-Kontexte des aktuellen Administrator-Cookies ab, sperrt ihre edid-Werte und löscht das Login-Cookie. Falls kein Multi-Login-Cookie vorliegt, wird der gewählte Kontext abgemeldet. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Gewählter Kontext, logout=true und Liste aller abgemeldeten edid-Werte.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edid":"edid_synthetic_login_context","logout":true,"logout_edids":["edid_synthetic_login_context"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"edid":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"logout":{"type":"boolean"},"logout_edids":{"type":"array","items":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]}}},"additionalProperties":true,"required":["edid","logout","logout_edids"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"data":{"type":"object","properties":{"expire":{"type":["string","number"]},"reply":{"type":"string"},"remember":{"description":"Kanonisch Boolean; native PHP-Normalisierung gilt."}},"additionalProperties":true}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Alle Login-Kontexte abmelden","value":{"id":"edid_synthetic_login_context","data":{}}}}}}},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":218}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/list/logout_edid Einzelnen Login-Kontext abmelden Meldet nur den gewählten edid-Kontext ab. Verbleibende gültige Kontexte werden in ein erneuertes Cookie übernommen; beim letzten Kontext wird das Cookie gelöscht. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen. - Operation-ID: put_v2_auth_list_logout_edid - Dokumentation: /docs/api/v2/operations/put-v2-auth-list-logout-edid-a3de692f8831/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "edid_synthetic_login_context", "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/list/logout_edid' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/list/logout_edid"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "edid_synthetic_login_context", "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/list/logout_edid" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"edid_synthetic_login_context\",\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_list_logout_edid","summary":"Einzelnen Login-Kontext abmelden","description":"Meldet nur den gewählten edid-Kontext ab. Verbleibende gültige Kontexte werden in ein erneuertes Cookie übernommen; beim letzten Kontext wird das Cookie gelöscht. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Letzter Kontext: Logout-Bestätigung; andernfalls erneuerte Login-Antwort mit verbleibendem edid und logout_edids.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edid":"edid_synthetic_login_context","logout":true,"logout_edids":["edid_synthetic_login_context"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"edid":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"logout":{"type":"boolean"},"logout_edids":{"type":"array","items":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]}},"login":{"type":"object","additionalProperties":true},"token":{"type":["boolean","string"],"x-edoobox-sensitive":true},"cookie":{"type":["boolean","string"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"data":{"type":"object","properties":{"expire":{"type":["string","number"]},"reply":{"type":"string"},"remember":{"description":"Kanonisch Boolean; native PHP-Normalisierung gilt."}},"additionalProperties":true}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Einzelnen Login-Kontext abmelden","value":{"id":"edid_synthetic_login_context","data":{}}}}}}},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":221}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/list/refresh Login-Sitzung verlängern Verlängert die aktuelle, weiterhin gültige Sitzung und prüft Rechte und Kontozugehörigkeit erneut. Administratoren benötigen gegebenenfalls eine bereits abgeschlossene 2FA; remember bestimmt 30 Tage bzw. 30 Minuten, API-Logins können data.expire verwenden. Der aktuelle Header-edid bestimmt den zu erneuernden Kontext. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen. - Operation-ID: put_v2_auth_list_refresh - Dokumentation: /docs/api/v2/operations/put-v2-auth-list-refresh-d288e58f3f2f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "edid_synthetic_login_context", "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/list/refresh' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/list/refresh"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "edid_synthetic_login_context", "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/list/refresh" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"edid_synthetic_login_context\",\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_list_refresh","summary":"Login-Sitzung verlängern","description":"Verlängert die aktuelle, weiterhin gültige Sitzung und prüft Rechte und Kontozugehörigkeit erneut. Administratoren benötigen gegebenenfalls eine bereits abgeschlossene 2FA; remember bestimmt 30 Tage bzw. 30 Minuten, API-Logins können data.expire verwenden. Der aktuelle Header-edid bestimmt den zu erneuernden Kontext. auth-Level 3/4 oder die native Erlaubnis für den eigenen Kontext ist erforderlich. Beim /list-Alias muss id im Request stehen.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Erneuerte grantabhängige Login-Antwort mit edid und login, Cookie bzw. API-access_token; Fehler bei abgelaufenem oder gesperrtem Kontext.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"auth_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Opaker Login-Kontext edid aus der Authentifizierungsantwort. Keine Objekt- oder Datenbank-ID.","examples":["edid_synthetic_login_context"]},"data":{"type":"object","properties":{"expire":{"type":["string","number"]},"reply":{"type":"string"},"remember":{"description":"Kanonisch Boolean; native PHP-Normalisierung gilt."}},"additionalProperties":true}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Login-Sitzung verlängern","value":{"id":"edid_synthetic_login_context","data":{}}}}}}},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":224}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/auth/list/skip_counter Zahlungshinweis einmal überspringen Verbraucht, sofern vorhanden, einen verbleibenden Überspring-Schritt des gewählten Kontos und protokolliert den Administrator. Der Konto-Kontext stammt aus der authentifizierten Sitzung; kein frei wählbares Fremdkonto. Der Dispatcher verlangt einen id-Selektor, auch beim /list-Alias; /skip liefert ihn über das Pfadsegment. - Operation-ID: put_v2_auth_list_skip_counter - Dokumentation: /docs/api/v2/operations/put-v2-auth-list-skip-counter-ae9bd6c155fc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "skip" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/list/skip_counter' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/list/skip_counter"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "skip" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/list/skip_counter" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"skip\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_list_skip_counter","summary":"Zahlungshinweis einmal überspringen","description":"Verbraucht, sofern vorhanden, einen verbleibenden Überspring-Schritt des gewählten Kontos und protokolliert den Administrator. Der Konto-Kontext stammt aus der authentifizierten Sitzung; kein frei wählbares Fremdkonto. Der Dispatcher verlangt einen id-Selektor, auch beim /list-Alias; /skip liefert ihn über das Pfadsegment.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Konto-ID, update und verbleibender skip_counter; bei tatsächlichem Verbrauch zusätzlich öffentliche skip-ID und opaker token.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":false,"skip_counter":0}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"update":{"type":"boolean"},"skip_counter":{"type":"integer"},"skip":{"type":"string","pattern":"^skip_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"token":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."}},"additionalProperties":true,"required":["id","update","skip_counter"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Edsubscriptionfunc.php","line":295},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Zahlungshinweis einmal überspringen","value":{"id":"skip"}}}}},"required":true},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":206}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/auth/oauth2/microsoft_calendar/logout Leeren microsoft_calendar-Logout-Callback aufrufen Vorhandener Legacy-Callback: der Handler kehrt ohne Provider-Abmeldung, Änderung der Verbindung oder fachlichen Antwortpayload zurück. Zum Trennen einer Verbindung den dafür vorgesehenen authentifizierten Einstellungsablauf verwenden. - Operation-ID: get_v2_auth_oauth2_microsoft_calendar_logout - Dokumentation: /docs/api/v2/operations/get-v2-auth-oauth2-microsoft-calendar-logout-3d762ecfc4d9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/microsoft_calendar/logout' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/microsoft_calendar/logout"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/microsoft_calendar/logout" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_oauth2_microsoft_calendar_logout","summary":"Leeren microsoft_calendar-Logout-Callback aufrufen","description":"Vorhandener Legacy-Callback: der Handler kehrt ohne Provider-Abmeldung, Änderung der Verbindung oder fachlichen Antwortpayload zurück. Zum Trennen einer Verbindung den dafür vorgesehenen authentifizierten Einstellungsablauf verwenden.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"Leere Antwort; der Callback-Handler führt keine Abmeldung aus."},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/oauth2/microsoft_calendar/logout Leeren microsoft_calendar-Logout-Callback aufrufen Vorhandener Legacy-Callback: der Handler kehrt ohne Provider-Abmeldung, Änderung der Verbindung oder fachlichen Antwortpayload zurück. Zum Trennen einer Verbindung den dafür vorgesehenen authentifizierten Einstellungsablauf verwenden. - Operation-ID: post_v2_auth_oauth2_microsoft_calendar_logout - Dokumentation: /docs/api/v2/operations/post-v2-auth-oauth2-microsoft-calendar-logout-3c504d6887bd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/microsoft_calendar/logout' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/microsoft_calendar/logout"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/microsoft_calendar/logout" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_oauth2_microsoft_calendar_logout","summary":"Leeren microsoft_calendar-Logout-Callback aufrufen","description":"Vorhandener Legacy-Callback: der Handler kehrt ohne Provider-Abmeldung, Änderung der Verbindung oder fachlichen Antwortpayload zurück. Zum Trennen einer Verbindung den dafür vorgesehenen authentifizierten Einstellungsablauf verwenden.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"Leere Antwort; der Callback-Handler führt keine Abmeldung aus."},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/oauth2/microsoft_calendar/redirect Microsoft-Supportkalender-Verbindung abschließen Verarbeitet ausschließlich den Microsoft-Rückruf aus der zuvor gestarteten Support-Anmeldung und speichert die Kalender-Verbindung für die gebundene Support-Sitzung. state ist ein einmaliger, zehn Minuten gültiger 64-stelliger Hex-Wert mit serverseitiger Browser-Cookie-Bindung; aktuelle Rechte bzw. Support-Sitzung werden erneut geprüft. Query-code und Query-state werden auch beim POST-Alias verwendet. Abschließend folgt eine Browser-Weiterleitung; kein freier Konto- oder Token-Import. - Operation-ID: get_v2_auth_oauth2_microsoft_calendar_redirect - Dokumentation: /docs/api/v2/operations/get-v2-auth-oauth2-microsoft-calendar-redirect-a0e2c7ba7e21/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | nein | Fehlender oder ungültiger Code führt zur Fehlerweiterleitung. | | state | query | ja | Einmaliger browsergebundener OAuth-State. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Microsoft-Autorisierungscode; nur aus dem vorherigen Browser-Ablauf."},"description":"Fehlender oder ungültiger Code führt zur Fehlerweiterleitung."},{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_STATE:?QUERY_STATE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/microsoft_calendar/redirect' \ --url-query 'state='"${QUERY_STATE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/microsoft_calendar/redirect"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("state", required("QUERY_STATE")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/microsoft_calendar/redirect" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("state", os.environ["QUERY_STATE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_oauth2_microsoft_calendar_redirect","summary":"Microsoft-Supportkalender-Verbindung abschließen","description":"Verarbeitet ausschließlich den Microsoft-Rückruf aus der zuvor gestarteten Support-Anmeldung und speichert die Kalender-Verbindung für die gebundene Support-Sitzung. state ist ein einmaliger, zehn Minuten gültiger 64-stelliger Hex-Wert mit serverseitiger Browser-Cookie-Bindung; aktuelle Rechte bzw. Support-Sitzung werden erneut geprüft. Query-code und Query-state werden auch beim POST-Alias verwendet. Abschließend folgt eine Browser-Weiterleitung; kein freier Konto- oder Token-Import.","tags":["auth"],"security":[],"parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Microsoft-Autorisierungscode; nur aus dem vorherigen Browser-Ablauf."},"description":"Fehlender oder ungültiger Code führt zur Fehlerweiterleitung."},{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."}],"responses":{"200":{"description":"Browser-Weiterleitung zur Mail- bzw. Supportansicht mit Erfolgs-/Fehlerstatus."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}}},"x-edoobox-mutates-state":true,"x-edoobox-auth-boundarySource":{"file":"application/models/v2/auth/Microsoftcalendar.php","line":14,"reason":"Reads code/state from query for both HTTP methods, validates the OAuth state through the provider handler, and writes a Location redirect with an empty body."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/oauth2/microsoft_calendar/redirect Microsoft-Supportkalender-Verbindung abschließen Verarbeitet ausschließlich den Microsoft-Rückruf aus der zuvor gestarteten Support-Anmeldung und speichert die Kalender-Verbindung für die gebundene Support-Sitzung. state ist ein einmaliger, zehn Minuten gültiger 64-stelliger Hex-Wert mit serverseitiger Browser-Cookie-Bindung; aktuelle Rechte bzw. Support-Sitzung werden erneut geprüft. Query-code und Query-state werden auch beim POST-Alias verwendet. Abschließend folgt eine Browser-Weiterleitung; kein freier Konto- oder Token-Import. - Operation-ID: post_v2_auth_oauth2_microsoft_calendar_redirect - Dokumentation: /docs/api/v2/operations/post-v2-auth-oauth2-microsoft-calendar-redirect-6023a44efbea/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | nein | Fehlender oder ungültiger Code führt zur Fehlerweiterleitung. | | state | query | ja | Einmaliger browsergebundener OAuth-State. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Microsoft-Autorisierungscode; nur aus dem vorherigen Browser-Ablauf."},"description":"Fehlender oder ungültiger Code führt zur Fehlerweiterleitung."},{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_STATE:?QUERY_STATE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/microsoft_calendar/redirect' \ --url-query 'state='"${QUERY_STATE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/microsoft_calendar/redirect"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("state", required("QUERY_STATE")); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/microsoft_calendar/redirect" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("state", os.environ["QUERY_STATE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_oauth2_microsoft_calendar_redirect","summary":"Microsoft-Supportkalender-Verbindung abschließen","description":"Verarbeitet ausschließlich den Microsoft-Rückruf aus der zuvor gestarteten Support-Anmeldung und speichert die Kalender-Verbindung für die gebundene Support-Sitzung. state ist ein einmaliger, zehn Minuten gültiger 64-stelliger Hex-Wert mit serverseitiger Browser-Cookie-Bindung; aktuelle Rechte bzw. Support-Sitzung werden erneut geprüft. Query-code und Query-state werden auch beim POST-Alias verwendet. Abschließend folgt eine Browser-Weiterleitung; kein freier Konto- oder Token-Import.","tags":["auth"],"security":[],"parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Microsoft-Autorisierungscode; nur aus dem vorherigen Browser-Ablauf."},"description":"Fehlender oder ungültiger Code führt zur Fehlerweiterleitung."},{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."}],"responses":{"200":{"description":"Browser-Weiterleitung zur Mail- bzw. Supportansicht mit Erfolgs-/Fehlerstatus."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/auth/Microsoftcalendar.php","line":14},"x-edoobox-auth-boundarySource":{"file":"application/models/v2/auth/Microsoftcalendar.php","line":14,"reason":"Reads code/state from query for both HTTP methods, validates the OAuth state through the provider handler, and writes a Location redirect with an empty body."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/oauth2/microsoft/logout Leeren microsoft-Logout-Callback aufrufen Vorhandener Legacy-Callback: der Handler kehrt ohne Provider-Abmeldung, Änderung der Verbindung oder fachlichen Antwortpayload zurück. Zum Trennen einer Verbindung den dafür vorgesehenen authentifizierten Einstellungsablauf verwenden. - Operation-ID: get_v2_auth_oauth2_microsoft_logout - Dokumentation: /docs/api/v2/operations/get-v2-auth-oauth2-microsoft-logout-2c196f165b2a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/microsoft/logout' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/microsoft/logout"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/microsoft/logout" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_oauth2_microsoft_logout","summary":"Leeren microsoft-Logout-Callback aufrufen","description":"Vorhandener Legacy-Callback: der Handler kehrt ohne Provider-Abmeldung, Änderung der Verbindung oder fachlichen Antwortpayload zurück. Zum Trennen einer Verbindung den dafür vorgesehenen authentifizierten Einstellungsablauf verwenden.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"Leere Antwort; der Callback-Handler führt keine Abmeldung aus."},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/oauth2/microsoft/logout Leeren microsoft-Logout-Callback aufrufen Vorhandener Legacy-Callback: der Handler kehrt ohne Provider-Abmeldung, Änderung der Verbindung oder fachlichen Antwortpayload zurück. Zum Trennen einer Verbindung den dafür vorgesehenen authentifizierten Einstellungsablauf verwenden. - Operation-ID: post_v2_auth_oauth2_microsoft_logout - Dokumentation: /docs/api/v2/operations/post-v2-auth-oauth2-microsoft-logout-b536abfc3f45/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/microsoft/logout' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/microsoft/logout"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/microsoft/logout" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_oauth2_microsoft_logout","summary":"Leeren microsoft-Logout-Callback aufrufen","description":"Vorhandener Legacy-Callback: der Handler kehrt ohne Provider-Abmeldung, Änderung der Verbindung oder fachlichen Antwortpayload zurück. Zum Trennen einer Verbindung den dafür vorgesehenen authentifizierten Einstellungsablauf verwenden.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"Leere Antwort; der Callback-Handler führt keine Abmeldung aus."},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/oauth2/microsoft/redirect Microsoft-Mailkonto-Verbindung abschließen Tauscht den Microsoft-Code aus dem vorherigen Mail-Verbindungsablauf und speichert die validierte Verbindung im gebundenen Konto. Alte Verbindungen werden erst beim erfolgreichen Ersetzen deaktiviert. state ist ein einmaliger, zehn Minuten gültiger 64-stelliger Hex-Wert mit serverseitiger Browser-Cookie-Bindung; aktuelle Rechte bzw. Support-Sitzung werden erneut geprüft. Query-code und Query-state werden auch beim POST-Alias verwendet. Abschließend folgt eine Browser-Weiterleitung; kein freier Konto- oder Token-Import. - Operation-ID: get_v2_auth_oauth2_microsoft_redirect - Dokumentation: /docs/api/v2/operations/get-v2-auth-oauth2-microsoft-redirect-924eaa7e829a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | nein | Fehlender oder ungültiger Code führt zur Fehlerweiterleitung. | | state | query | ja | Einmaliger browsergebundener OAuth-State. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Microsoft-Autorisierungscode; nur aus dem vorherigen Browser-Ablauf."},"description":"Fehlender oder ungültiger Code führt zur Fehlerweiterleitung."},{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_STATE:?QUERY_STATE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/microsoft/redirect' \ --url-query 'state='"${QUERY_STATE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/microsoft/redirect"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("state", required("QUERY_STATE")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/microsoft/redirect" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("state", os.environ["QUERY_STATE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_oauth2_microsoft_redirect","summary":"Microsoft-Mailkonto-Verbindung abschließen","description":"Tauscht den Microsoft-Code aus dem vorherigen Mail-Verbindungsablauf und speichert die validierte Verbindung im gebundenen Konto. Alte Verbindungen werden erst beim erfolgreichen Ersetzen deaktiviert. state ist ein einmaliger, zehn Minuten gültiger 64-stelliger Hex-Wert mit serverseitiger Browser-Cookie-Bindung; aktuelle Rechte bzw. Support-Sitzung werden erneut geprüft. Query-code und Query-state werden auch beim POST-Alias verwendet. Abschließend folgt eine Browser-Weiterleitung; kein freier Konto- oder Token-Import.","tags":["auth"],"security":[],"parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Microsoft-Autorisierungscode; nur aus dem vorherigen Browser-Ablauf."},"description":"Fehlender oder ungültiger Code führt zur Fehlerweiterleitung."},{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."}],"responses":{"200":{"description":"Browser-Weiterleitung zur Mail- bzw. Supportansicht mit Erfolgs-/Fehlerstatus."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}}},"x-edoobox-mutates-state":true,"x-edoobox-auth-boundarySource":{"file":"application/models/v2/auth/Microsoft.php","line":33,"reason":"Reads code/state from query for both HTTP methods, validates the OAuth state through the provider handler, and writes a Location redirect with an empty body."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/oauth2/microsoft/redirect Microsoft-Mailkonto-Verbindung abschließen Tauscht den Microsoft-Code aus dem vorherigen Mail-Verbindungsablauf und speichert die validierte Verbindung im gebundenen Konto. Alte Verbindungen werden erst beim erfolgreichen Ersetzen deaktiviert. state ist ein einmaliger, zehn Minuten gültiger 64-stelliger Hex-Wert mit serverseitiger Browser-Cookie-Bindung; aktuelle Rechte bzw. Support-Sitzung werden erneut geprüft. Query-code und Query-state werden auch beim POST-Alias verwendet. Abschließend folgt eine Browser-Weiterleitung; kein freier Konto- oder Token-Import. - Operation-ID: post_v2_auth_oauth2_microsoft_redirect - Dokumentation: /docs/api/v2/operations/post-v2-auth-oauth2-microsoft-redirect-33a99ee4e60e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | code | query | nein | Fehlender oder ungültiger Code führt zur Fehlerweiterleitung. | | state | query | ja | Einmaliger browsergebundener OAuth-State. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Microsoft-Autorisierungscode; nur aus dem vorherigen Browser-Ablauf."},"description":"Fehlender oder ungültiger Code führt zur Fehlerweiterleitung."},{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_STATE:?QUERY_STATE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/microsoft/redirect' \ --url-query 'state='"${QUERY_STATE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/microsoft/redirect"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("state", required("QUERY_STATE")); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/microsoft/redirect" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("state", os.environ["QUERY_STATE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_oauth2_microsoft_redirect","summary":"Microsoft-Mailkonto-Verbindung abschließen","description":"Tauscht den Microsoft-Code aus dem vorherigen Mail-Verbindungsablauf und speichert die validierte Verbindung im gebundenen Konto. Alte Verbindungen werden erst beim erfolgreichen Ersetzen deaktiviert. state ist ein einmaliger, zehn Minuten gültiger 64-stelliger Hex-Wert mit serverseitiger Browser-Cookie-Bindung; aktuelle Rechte bzw. Support-Sitzung werden erneut geprüft. Query-code und Query-state werden auch beim POST-Alias verwendet. Abschließend folgt eine Browser-Weiterleitung; kein freier Konto- oder Token-Import.","tags":["auth"],"security":[],"parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Microsoft-Autorisierungscode; nur aus dem vorherigen Browser-Ablauf."},"description":"Fehlender oder ungültiger Code führt zur Fehlerweiterleitung."},{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."}],"responses":{"200":{"description":"Browser-Weiterleitung zur Mail- bzw. Supportansicht mit Erfolgs-/Fehlerstatus."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/auth/Microsoft.php","line":33},"x-edoobox-auth-boundarySource":{"file":"application/models/v2/auth/Microsoft.php","line":33,"reason":"Reads code/state from query for both HTTP methods, validates the OAuth state through the provider handler, and writes a Location redirect with an empty body."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/oauth2/paypal PayPal-Händlerverbindung abschließen Liest Query-merchantIdInPayPal, lädt die Händlerintegration serverseitig von PayPal und ordnet sie anhand der verifizierten tracking_id dem Konto zu. Speichert die Verbindung und leitet zur Zahlungsanbieter-Einstellung weiter. POST liest dieselbe Query; keine freie Konto-ID und kein allgemeiner JSON-data-Envelope. - Operation-ID: get_v2_auth_oauth2_paypal - Dokumentation: /docs/api/v2/operations/get-v2-auth-oauth2-paypal-d12c2488d293/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | merchantIdInPayPal | query | ja | PayPal-Händler-ID aus dem Provider-Rückruf. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"merchantIdInPayPal","in":"query","required":true,"schema":{"type":"string"},"description":"PayPal-Händler-ID aus dem Provider-Rückruf."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_MERCHANTIDINPAYPAL:?QUERY_MERCHANTIDINPAYPAL setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/paypal' \ --url-query 'merchantIdInPayPal='"${QUERY_MERCHANTIDINPAYPAL}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/paypal"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("merchantIdInPayPal", required("QUERY_MERCHANTIDINPAYPAL")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/paypal" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("merchantIdInPayPal", os.environ["QUERY_MERCHANTIDINPAYPAL"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_oauth2_paypal","summary":"PayPal-Händlerverbindung abschließen","description":"Liest Query-merchantIdInPayPal, lädt die Händlerintegration serverseitig von PayPal und ordnet sie anhand der verifizierten tracking_id dem Konto zu. Speichert die Verbindung und leitet zur Zahlungsanbieter-Einstellung weiter. POST liest dieselbe Query; keine freie Konto-ID und kein allgemeiner JSON-data-Envelope.","tags":["auth"],"security":[],"parameters":[{"name":"merchantIdInPayPal","in":"query","required":true,"schema":{"type":"string"},"description":"PayPal-Händler-ID aus dem Provider-Rückruf."}],"responses":{"200":{"description":"Browser-Weiterleitung mit paypal_connect=1 bzw. paypal_error=token_invalid."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/oauth2/paypal PayPal-Händlerverbindung abschließen Liest Query-merchantIdInPayPal, lädt die Händlerintegration serverseitig von PayPal und ordnet sie anhand der verifizierten tracking_id dem Konto zu. Speichert die Verbindung und leitet zur Zahlungsanbieter-Einstellung weiter. POST liest dieselbe Query; keine freie Konto-ID und kein allgemeiner JSON-data-Envelope. - Operation-ID: post_v2_auth_oauth2_paypal - Dokumentation: /docs/api/v2/operations/post-v2-auth-oauth2-paypal-cd044d9b8bc4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | merchantIdInPayPal | query | ja | PayPal-Händler-ID aus dem Provider-Rückruf. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"merchantIdInPayPal","in":"query","required":true,"schema":{"type":"string"},"description":"PayPal-Händler-ID aus dem Provider-Rückruf."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_MERCHANTIDINPAYPAL:?QUERY_MERCHANTIDINPAYPAL setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/paypal' \ --url-query 'merchantIdInPayPal='"${QUERY_MERCHANTIDINPAYPAL}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/paypal"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("merchantIdInPayPal", required("QUERY_MERCHANTIDINPAYPAL")); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/paypal" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("merchantIdInPayPal", os.environ["QUERY_MERCHANTIDINPAYPAL"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_oauth2_paypal","summary":"PayPal-Händlerverbindung abschließen","description":"Liest Query-merchantIdInPayPal, lädt die Händlerintegration serverseitig von PayPal und ordnet sie anhand der verifizierten tracking_id dem Konto zu. Speichert die Verbindung und leitet zur Zahlungsanbieter-Einstellung weiter. POST liest dieselbe Query; keine freie Konto-ID und kein allgemeiner JSON-data-Envelope.","tags":["auth"],"security":[],"parameters":[{"name":"merchantIdInPayPal","in":"query","required":true,"schema":{"type":"string"},"description":"PayPal-Händler-ID aus dem Provider-Rückruf."}],"responses":{"200":{"description":"Browser-Weiterleitung mit paypal_connect=1 bzw. paypal_error=token_invalid."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/oauth2/paypal_bridge PayPal-Händlerverbindung für die Legacy-Ansicht abschließen Liest Query-merchantIdInPayPal, lädt die Händlerintegration serverseitig von PayPal und ordnet sie anhand der verifizierten tracking_id dem Konto zu. Speichert die Verbindung und leitet zur Zahlungsanbieter-Einstellung weiter. POST liest dieselbe Query; keine freie Konto-ID und kein allgemeiner JSON-data-Envelope. - Operation-ID: get_v2_auth_oauth2_paypal_bridge - Dokumentation: /docs/api/v2/operations/get-v2-auth-oauth2-paypal-bridge-f1bde4de77a0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | merchantIdInPayPal | query | ja | PayPal-Händler-ID aus dem Provider-Rückruf. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"merchantIdInPayPal","in":"query","required":true,"schema":{"type":"string"},"description":"PayPal-Händler-ID aus dem Provider-Rückruf."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_MERCHANTIDINPAYPAL:?QUERY_MERCHANTIDINPAYPAL setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/paypal_bridge' \ --url-query 'merchantIdInPayPal='"${QUERY_MERCHANTIDINPAYPAL}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/paypal_bridge"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("merchantIdInPayPal", required("QUERY_MERCHANTIDINPAYPAL")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/paypal_bridge" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("merchantIdInPayPal", os.environ["QUERY_MERCHANTIDINPAYPAL"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_oauth2_paypal_bridge","summary":"PayPal-Händlerverbindung für die Legacy-Ansicht abschließen","description":"Liest Query-merchantIdInPayPal, lädt die Händlerintegration serverseitig von PayPal und ordnet sie anhand der verifizierten tracking_id dem Konto zu. Speichert die Verbindung und leitet zur Zahlungsanbieter-Einstellung weiter. POST liest dieselbe Query; keine freie Konto-ID und kein allgemeiner JSON-data-Envelope.","tags":["auth"],"security":[],"parameters":[{"name":"merchantIdInPayPal","in":"query","required":true,"schema":{"type":"string"},"description":"PayPal-Händler-ID aus dem Provider-Rückruf."}],"responses":{"200":{"description":"Browser-Weiterleitung mit paypal_connect=1 bzw. paypal_error=token_invalid."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/oauth2/paypal_bridge PayPal-Händlerverbindung für die Legacy-Ansicht abschließen Liest Query-merchantIdInPayPal, lädt die Händlerintegration serverseitig von PayPal und ordnet sie anhand der verifizierten tracking_id dem Konto zu. Speichert die Verbindung und leitet zur Zahlungsanbieter-Einstellung weiter. POST liest dieselbe Query; keine freie Konto-ID und kein allgemeiner JSON-data-Envelope. - Operation-ID: post_v2_auth_oauth2_paypal_bridge - Dokumentation: /docs/api/v2/operations/post-v2-auth-oauth2-paypal-bridge-d5cf7793db01/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | merchantIdInPayPal | query | ja | PayPal-Händler-ID aus dem Provider-Rückruf. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"merchantIdInPayPal","in":"query","required":true,"schema":{"type":"string"},"description":"PayPal-Händler-ID aus dem Provider-Rückruf."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_MERCHANTIDINPAYPAL:?QUERY_MERCHANTIDINPAYPAL setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/paypal_bridge' \ --url-query 'merchantIdInPayPal='"${QUERY_MERCHANTIDINPAYPAL}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/paypal_bridge"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("merchantIdInPayPal", required("QUERY_MERCHANTIDINPAYPAL")); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/paypal_bridge" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("merchantIdInPayPal", os.environ["QUERY_MERCHANTIDINPAYPAL"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_oauth2_paypal_bridge","summary":"PayPal-Händlerverbindung für die Legacy-Ansicht abschließen","description":"Liest Query-merchantIdInPayPal, lädt die Händlerintegration serverseitig von PayPal und ordnet sie anhand der verifizierten tracking_id dem Konto zu. Speichert die Verbindung und leitet zur Zahlungsanbieter-Einstellung weiter. POST liest dieselbe Query; keine freie Konto-ID und kein allgemeiner JSON-data-Envelope.","tags":["auth"],"security":[],"parameters":[{"name":"merchantIdInPayPal","in":"query","required":true,"schema":{"type":"string"},"description":"PayPal-Händler-ID aus dem Provider-Rückruf."}],"responses":{"200":{"description":"Browser-Weiterleitung mit paypal_connect=1 bzw. paypal_error=token_invalid."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/oauth2/stripe Stripe-Connect-Autorisierung abschließen Verarbeitet den Stripe-OAuth-Rückruf aus dem vorherigen autorisierten Verbindungsablauf. Verbraucht den browsergebundenen State vor dem Codeaustausch; aktuelle Zahlungsverbindungsrechte werden erneut geprüft. Bei scope=read_write und gültigem Code wird die Verbindung gespeichert und gegebenenfalls die bisherige andere Stripe-Verbindung widerrufen. Query-Parameter werden auch bei POST gelesen, anschließend Browser-Weiterleitung. - Operation-ID: get_v2_auth_oauth2_stripe - Dokumentation: /docs/api/v2/operations/get-v2-auth-oauth2-stripe-55d25cde83ea/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | state | query | ja | Einmaliger browsergebundener OAuth-State. | | code | query | nein | Provider-Autorisierungscode; für Erfolg erforderlich. | | scope | query | nein | Für eine erfolgreiche Verbindung erforderlich. | | error | query | nein | Provider-Fehler führt zur Fehlerweiterleitung. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."},{"name":"code","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Provider-Autorisierungscode; für Erfolg erforderlich."},{"name":"scope","in":"query","required":false,"schema":{"type":"string","const":"read_write"},"description":"Für eine erfolgreiche Verbindung erforderlich."},{"name":"error","in":"query","required":false,"schema":{"type":"string"},"description":"Provider-Fehler führt zur Fehlerweiterleitung."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_STATE:?QUERY_STATE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/stripe' \ --url-query 'state='"${QUERY_STATE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/stripe"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("state", required("QUERY_STATE")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/stripe" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("state", os.environ["QUERY_STATE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_oauth2_stripe","summary":"Stripe-Connect-Autorisierung abschließen","description":"Verarbeitet den Stripe-OAuth-Rückruf aus dem vorherigen autorisierten Verbindungsablauf. Verbraucht den browsergebundenen State vor dem Codeaustausch; aktuelle Zahlungsverbindungsrechte werden erneut geprüft. Bei scope=read_write und gültigem Code wird die Verbindung gespeichert und gegebenenfalls die bisherige andere Stripe-Verbindung widerrufen. Query-Parameter werden auch bei POST gelesen, anschließend Browser-Weiterleitung.","tags":["auth"],"security":[],"parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."},{"name":"code","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Provider-Autorisierungscode; für Erfolg erforderlich."},{"name":"scope","in":"query","required":false,"schema":{"type":"string","const":"read_write"},"description":"Für eine erfolgreiche Verbindung erforderlich."},{"name":"error","in":"query","required":false,"schema":{"type":"string"},"description":"Provider-Fehler führt zur Fehlerweiterleitung."}],"responses":{"200":{"description":"Browser-Weiterleitung zur Zahlungsanbieter-Einstellung."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/oauth2/stripe Stripe-Connect-Autorisierung abschließen Verarbeitet den Stripe-OAuth-Rückruf aus dem vorherigen autorisierten Verbindungsablauf. Verbraucht den browsergebundenen State vor dem Codeaustausch; aktuelle Zahlungsverbindungsrechte werden erneut geprüft. Bei scope=read_write und gültigem Code wird die Verbindung gespeichert und gegebenenfalls die bisherige andere Stripe-Verbindung widerrufen. Query-Parameter werden auch bei POST gelesen, anschließend Browser-Weiterleitung. - Operation-ID: post_v2_auth_oauth2_stripe - Dokumentation: /docs/api/v2/operations/post-v2-auth-oauth2-stripe-5aa3564f16f6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | state | query | ja | Einmaliger browsergebundener OAuth-State. | | code | query | nein | Provider-Autorisierungscode; für Erfolg erforderlich. | | scope | query | nein | Für eine erfolgreiche Verbindung erforderlich. | | error | query | nein | Provider-Fehler führt zur Fehlerweiterleitung. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."},{"name":"code","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Provider-Autorisierungscode; für Erfolg erforderlich."},{"name":"scope","in":"query","required":false,"schema":{"type":"string","const":"read_write"},"description":"Für eine erfolgreiche Verbindung erforderlich."},{"name":"error","in":"query","required":false,"schema":{"type":"string"},"description":"Provider-Fehler führt zur Fehlerweiterleitung."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_STATE:?QUERY_STATE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/oauth2/stripe' \ --url-query 'state='"${QUERY_STATE}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/oauth2/stripe"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("state", required("QUERY_STATE")); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/oauth2/stripe" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("state", os.environ["QUERY_STATE"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_oauth2_stripe","summary":"Stripe-Connect-Autorisierung abschließen","description":"Verarbeitet den Stripe-OAuth-Rückruf aus dem vorherigen autorisierten Verbindungsablauf. Verbraucht den browsergebundenen State vor dem Codeaustausch; aktuelle Zahlungsverbindungsrechte werden erneut geprüft. Bei scope=read_write und gültigem Code wird die Verbindung gespeichert und gegebenenfalls die bisherige andere Stripe-Verbindung widerrufen. Query-Parameter werden auch bei POST gelesen, anschließend Browser-Weiterleitung.","tags":["auth"],"security":[],"parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$","x-edoobox-sensitive":true},"description":"Einmaliger browsergebundener OAuth-State."},{"name":"code","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Provider-Autorisierungscode; für Erfolg erforderlich."},{"name":"scope","in":"query","required":false,"schema":{"type":"string","const":"read_write"},"description":"Für eine erfolgreiche Verbindung erforderlich."},{"name":"error","in":"query","required":false,"schema":{"type":"string"},"description":"Provider-Fehler führt zur Fehlerweiterleitung."}],"responses":{"200":{"description":"Browser-Weiterleitung zur Zahlungsanbieter-Einstellung."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/payment/stripe Stripe-Zahlungsrückkehr für Kontoguthaben verarbeiten Lädt den Query-payment_intent serverseitig von Stripe. Bei bestätigtem Erfolg wird die Zahlung einmal innerhalb der Cache-Sperrzeit als Kontogutschrift verarbeitet; anschließend Browser-Weiterleitung. Der GET-Alias kann somit Guthaben verändern. Zahlungsdaten aus dem Query allein gelten nicht als Zahlungsnachweis. - Operation-ID: get_v2_auth_payment_stripe - Dokumentation: /docs/api/v2/operations/get-v2-auth-payment-stripe-83ffa38c5f71/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | payment_intent | query | ja | Stripe-PaymentIntent-ID aus dem Zahlungsrücklauf. | | payment_intent_client_secret | query | nein | Legacy-Rückrufwert; der Handler lädt den Intent serverseitig, statt diesen Wert als Zahlungsnachweis zu verwenden. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"payment_intent","in":"query","required":true,"schema":{"type":"string"},"description":"Stripe-PaymentIntent-ID aus dem Zahlungsrücklauf."},{"name":"payment_intent_client_secret","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Legacy-Rückrufwert; der Handler lädt den Intent serverseitig, statt diesen Wert als Zahlungsnachweis zu verwenden."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_PAYMENT_INTENT:?QUERY_PAYMENT_INTENT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/payment/stripe' \ --url-query 'payment_intent='"${QUERY_PAYMENT_INTENT}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/payment/stripe"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("payment_intent", required("QUERY_PAYMENT_INTENT")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/payment/stripe" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("payment_intent", os.environ["QUERY_PAYMENT_INTENT"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_payment_stripe","summary":"Stripe-Zahlungsrückkehr für Kontoguthaben verarbeiten","description":"Lädt den Query-payment_intent serverseitig von Stripe. Bei bestätigtem Erfolg wird die Zahlung einmal innerhalb der Cache-Sperrzeit als Kontogutschrift verarbeitet; anschließend Browser-Weiterleitung. Der GET-Alias kann somit Guthaben verändern. Zahlungsdaten aus dem Query allein gelten nicht als Zahlungsnachweis.","tags":["auth"],"security":[],"parameters":[{"name":"payment_intent","in":"query","required":true,"schema":{"type":"string"},"description":"Stripe-PaymentIntent-ID aus dem Zahlungsrücklauf."},{"name":"payment_intent_client_secret","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Legacy-Rückrufwert; der Handler lädt den Intent serverseitig, statt diesen Wert als Zahlungsnachweis zu verwenden."}],"responses":{"200":{"description":"Browser-Weiterleitung zur Kontoabrechnung bzw. Legacy-Ansicht."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/payment/stripe Stripe-Zahlungsrückkehr für Kontoguthaben verarbeiten Lädt den Query-payment_intent serverseitig von Stripe. Bei bestätigtem Erfolg wird die Zahlung einmal innerhalb der Cache-Sperrzeit als Kontogutschrift verarbeitet; anschließend Browser-Weiterleitung. Der GET-Alias kann somit Guthaben verändern. Zahlungsdaten aus dem Query allein gelten nicht als Zahlungsnachweis. - Operation-ID: post_v2_auth_payment_stripe - Dokumentation: /docs/api/v2/operations/post-v2-auth-payment-stripe-0b77a4bfe245/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | payment_intent | query | ja | Stripe-PaymentIntent-ID aus dem Zahlungsrücklauf. | | payment_intent_client_secret | query | nein | Legacy-Rückrufwert; der Handler lädt den Intent serverseitig, statt diesen Wert als Zahlungsnachweis zu verwenden. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"payment_intent","in":"query","required":true,"schema":{"type":"string"},"description":"Stripe-PaymentIntent-ID aus dem Zahlungsrücklauf."},{"name":"payment_intent_client_secret","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Legacy-Rückrufwert; der Handler lädt den Intent serverseitig, statt diesen Wert als Zahlungsnachweis zu verwenden."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${QUERY_PAYMENT_INTENT:?QUERY_PAYMENT_INTENT setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/payment/stripe' \ --url-query 'payment_intent='"${QUERY_PAYMENT_INTENT}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/payment/stripe"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("payment_intent", required("QUERY_PAYMENT_INTENT")); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/payment/stripe" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("payment_intent", os.environ["QUERY_PAYMENT_INTENT"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_payment_stripe","summary":"Stripe-Zahlungsrückkehr für Kontoguthaben verarbeiten","description":"Lädt den Query-payment_intent serverseitig von Stripe. Bei bestätigtem Erfolg wird die Zahlung einmal innerhalb der Cache-Sperrzeit als Kontogutschrift verarbeitet; anschließend Browser-Weiterleitung. Der GET-Alias kann somit Guthaben verändern. Zahlungsdaten aus dem Query allein gelten nicht als Zahlungsnachweis.","tags":["auth"],"security":[],"parameters":[{"name":"payment_intent","in":"query","required":true,"schema":{"type":"string"},"description":"Stripe-PaymentIntent-ID aus dem Zahlungsrücklauf."},{"name":"payment_intent_client_secret","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Legacy-Rückrufwert; der Handler lädt den Intent serverseitig, statt diesen Wert als Zahlungsnachweis zu verwenden."}],"responses":{"200":{"description":"Browser-Weiterleitung zur Kontoabrechnung bzw. Legacy-Ansicht."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/saml/logout SAML-Einzelabmeldung verarbeiten Verarbeitet SAMLRequest oder SAMLResponse aus der Query (Base64 und DEFLATE), ermittelt den aktiven Provider und zugehörige Identität und beendet deren Administrator-/Teilnehmer-Sitzungen nach dem nativen SLO-Ablauf. Der POST-Alias liest ebenfalls Querywerte. Mindestens einer der beiden SAML-Werte muss zum Provider passen; es handelt sich um einen sensiblen Browser-/IdP-Rückruf mit GET-Änderungswirkung. - Operation-ID: get_v2_auth_saml_logout - Dokumentation: /docs/api/v2/operations/get-v2-auth-saml-logout-8c92a1022ebe/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | SAMLRequest | query | nein | SAML-Logout-Anfrage; alternativ SAMLResponse. | | SAMLResponse | query | nein | SAML-Logout-Antwort; alternativ SAMLRequest. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"SAMLRequest","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"SAML-Logout-Anfrage; alternativ SAMLResponse."},{"name":"SAMLResponse","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"SAML-Logout-Antwort; alternativ SAMLRequest."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/saml/logout' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/saml/logout"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/saml/logout" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_saml_logout","summary":"SAML-Einzelabmeldung verarbeiten","description":"Verarbeitet SAMLRequest oder SAMLResponse aus der Query (Base64 und DEFLATE), ermittelt den aktiven Provider und zugehörige Identität und beendet deren Administrator-/Teilnehmer-Sitzungen nach dem nativen SLO-Ablauf. Der POST-Alias liest ebenfalls Querywerte. Mindestens einer der beiden SAML-Werte muss zum Provider passen; es handelt sich um einen sensiblen Browser-/IdP-Rückruf mit GET-Änderungswirkung.","tags":["auth"],"security":[],"parameters":[{"name":"SAMLRequest","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"SAML-Logout-Anfrage; alternativ SAMLResponse."},{"name":"SAMLResponse","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"SAML-Logout-Antwort; alternativ SAMLRequest."}],"responses":{"200":{"description":"SLO-Browser-Antwort bzw. Weiterleitung; bei fehlender Sitzung/Provider normaler JSON-Fehler möglich."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/saml/logout SAML-Einzelabmeldung verarbeiten Verarbeitet SAMLRequest oder SAMLResponse aus der Query (Base64 und DEFLATE), ermittelt den aktiven Provider und zugehörige Identität und beendet deren Administrator-/Teilnehmer-Sitzungen nach dem nativen SLO-Ablauf. Der POST-Alias liest ebenfalls Querywerte. Mindestens einer der beiden SAML-Werte muss zum Provider passen; es handelt sich um einen sensiblen Browser-/IdP-Rückruf mit GET-Änderungswirkung. - Operation-ID: post_v2_auth_saml_logout - Dokumentation: /docs/api/v2/operations/post-v2-auth-saml-logout-6b6028292017/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | SAMLRequest | query | nein | SAML-Logout-Anfrage; alternativ SAMLResponse. | | SAMLResponse | query | nein | SAML-Logout-Antwort; alternativ SAMLRequest. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"SAMLRequest","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"SAML-Logout-Anfrage; alternativ SAMLResponse."},{"name":"SAMLResponse","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"SAML-Logout-Antwort; alternativ SAMLRequest."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/saml/logout' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/saml/logout"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/saml/logout" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_saml_logout","summary":"SAML-Einzelabmeldung verarbeiten","description":"Verarbeitet SAMLRequest oder SAMLResponse aus der Query (Base64 und DEFLATE), ermittelt den aktiven Provider und zugehörige Identität und beendet deren Administrator-/Teilnehmer-Sitzungen nach dem nativen SLO-Ablauf. Der POST-Alias liest ebenfalls Querywerte. Mindestens einer der beiden SAML-Werte muss zum Provider passen; es handelt sich um einen sensiblen Browser-/IdP-Rückruf mit GET-Änderungswirkung.","tags":["auth"],"security":[],"parameters":[{"name":"SAMLRequest","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"SAML-Logout-Anfrage; alternativ SAMLResponse."},{"name":"SAMLResponse","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"SAML-Logout-Antwort; alternativ SAMLRequest."}],"responses":{"200":{"description":"SLO-Browser-Antwort bzw. Weiterleitung; bei fehlender Sitzung/Provider normaler JSON-Fehler möglich."},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/saml/redirect/{providerId}/{accountId} SAML-Anmeldung beim Identitätsanbieter starten Prüft öffentliche Provider- und Konto-Pfad-ID sowie aktivierten Administrator-Loginbereich, erzeugt eine SAML-Anfrage und speichert deren serverseitige Korrelation. Setzt gegebenenfalls das _edsso-Cookie und liefert eine HTML-Weiterleitungsansicht zum IdP. Query-reply/state begleiten die App-Übergabe. Bereits GET erstellt damit einen Anmeldevorgang; dieser Browser-Schritt ist kein gewöhnlicher Datenabruf. - Operation-ID: get_v2_auth_saml_redirect_providerId_accountId - Dokumentation: /docs/api/v2/operations/get-v2-auth-saml-redirect-providerid-accountid-c9c6211ec77c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | providerId | path | ja | Öffentliche Provider-ID des Kontos. | | accountId | path | ja | Öffentliche Konto-ID. | | reply | query | nein | Gewünschter Login-Antwortmodus, etwa code für App-Übergabe. | | state | query | nein | Geschützter Übergabe-State für die App. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"providerId","in":"path","required":true,"schema":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["saml_012345abcdef_123456"]},"description":"Öffentliche Provider-ID des Kontos."},{"name":"accountId","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche Konto-ID."},{"name":"reply","in":"query","required":false,"schema":{"type":"string"},"description":"Gewünschter Login-Antwortmodus, etwa code für App-Übergabe."},{"name":"state","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Geschützter Übergabe-State für die App."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_PROVIDERID:?PATH_PROVIDERID setzen}" : "${PATH_ACCOUNTID:?PATH_ACCOUNTID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/saml/redirect/'"${PATH_PROVIDERID}"'/'"${PATH_ACCOUNTID}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/saml/redirect/{providerId}/{accountId}"; path = path.replaceAll("{providerId}", encodeURIComponent(required("PATH_PROVIDERID"))); path = path.replaceAll("{accountId}", encodeURIComponent(required("PATH_ACCOUNTID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/saml/redirect/{providerId}/{accountId}" path = path.replace("{providerId}", urllib.parse.quote(os.environ["PATH_PROVIDERID"], safe="")) path = path.replace("{accountId}", urllib.parse.quote(os.environ["PATH_ACCOUNTID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_saml_redirect_providerId_accountId","summary":"SAML-Anmeldung beim Identitätsanbieter starten","description":"Prüft öffentliche Provider- und Konto-Pfad-ID sowie aktivierten Administrator-Loginbereich, erzeugt eine SAML-Anfrage und speichert deren serverseitige Korrelation. Setzt gegebenenfalls das _edsso-Cookie und liefert eine HTML-Weiterleitungsansicht zum IdP. Query-reply/state begleiten die App-Übergabe. Bereits GET erstellt damit einen Anmeldevorgang; dieser Browser-Schritt ist kein gewöhnlicher Datenabruf.","tags":["auth"],"security":[],"parameters":[{"name":"providerId","in":"path","required":true,"schema":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["saml_012345abcdef_123456"]},"description":"Öffentliche Provider-ID des Kontos."},{"name":"accountId","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche Konto-ID."},{"name":"reply","in":"query","required":false,"schema":{"type":"string"},"description":"Gewünschter Login-Antwortmodus, etwa code für App-Übergabe."},{"name":"state","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Geschützter Übergabe-State für die App."}],"responses":{"200":{"description":"HTML-Weiterleitungsseite mit Refresh-Header zum IdP; Fehler können zur lokalen Loginansicht führen.","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Samlfunc.php","method":"loginRedirectIDP","line":1618},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/saml/redirect/{providerId}/{accountId} SAML-Anmeldung beim Identitätsanbieter starten Prüft öffentliche Provider- und Konto-Pfad-ID sowie aktivierten Administrator-Loginbereich, erzeugt eine SAML-Anfrage und speichert deren serverseitige Korrelation. Setzt gegebenenfalls das _edsso-Cookie und liefert eine HTML-Weiterleitungsansicht zum IdP. Query-reply/state begleiten die App-Übergabe. Bereits GET erstellt damit einen Anmeldevorgang; dieser Browser-Schritt ist kein gewöhnlicher Datenabruf. - Operation-ID: post_v2_auth_saml_redirect_providerId_accountId - Dokumentation: /docs/api/v2/operations/post-v2-auth-saml-redirect-providerid-accountid-92bd5582652b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | providerId | path | ja | Öffentliche Provider-ID des Kontos. | | accountId | path | ja | Öffentliche Konto-ID. | | reply | query | nein | Gewünschter Login-Antwortmodus, etwa code für App-Übergabe. | | state | query | nein | Geschützter Übergabe-State für die App. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"providerId","in":"path","required":true,"schema":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["saml_012345abcdef_123456"]},"description":"Öffentliche Provider-ID des Kontos."},{"name":"accountId","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche Konto-ID."},{"name":"reply","in":"query","required":false,"schema":{"type":"string"},"description":"Gewünschter Login-Antwortmodus, etwa code für App-Übergabe."},{"name":"state","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Geschützter Übergabe-State für die App."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_PROVIDERID:?PATH_PROVIDERID setzen}" : "${PATH_ACCOUNTID:?PATH_ACCOUNTID setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/saml/redirect/'"${PATH_PROVIDERID}"'/'"${PATH_ACCOUNTID}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/saml/redirect/{providerId}/{accountId}"; path = path.replaceAll("{providerId}", encodeURIComponent(required("PATH_PROVIDERID"))); path = path.replaceAll("{accountId}", encodeURIComponent(required("PATH_ACCOUNTID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/saml/redirect/{providerId}/{accountId}" path = path.replace("{providerId}", urllib.parse.quote(os.environ["PATH_PROVIDERID"], safe="")) path = path.replace("{accountId}", urllib.parse.quote(os.environ["PATH_ACCOUNTID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_saml_redirect_providerId_accountId","summary":"SAML-Anmeldung beim Identitätsanbieter starten","description":"Prüft öffentliche Provider- und Konto-Pfad-ID sowie aktivierten Administrator-Loginbereich, erzeugt eine SAML-Anfrage und speichert deren serverseitige Korrelation. Setzt gegebenenfalls das _edsso-Cookie und liefert eine HTML-Weiterleitungsansicht zum IdP. Query-reply/state begleiten die App-Übergabe. Bereits GET erstellt damit einen Anmeldevorgang; dieser Browser-Schritt ist kein gewöhnlicher Datenabruf.","tags":["auth"],"security":[],"parameters":[{"name":"providerId","in":"path","required":true,"schema":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["saml_012345abcdef_123456"]},"description":"Öffentliche Provider-ID des Kontos."},{"name":"accountId","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche Konto-ID."},{"name":"reply","in":"query","required":false,"schema":{"type":"string"},"description":"Gewünschter Login-Antwortmodus, etwa code für App-Übergabe."},{"name":"state","in":"query","required":false,"schema":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"description":"Geschützter Übergabe-State für die App."}],"responses":{"200":{"description":"HTML-Weiterleitungsseite mit Refresh-Header zum IdP; Fehler können zur lokalen Loginansicht führen.","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Samlfunc.php","method":"loginRedirectIDP","line":1618},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Samlfunc.php","method":"loginRedirectIDP","line":1618},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/saml/reply SAML-Identitätsantwort empfangen Empfängt SAMLResponse als Formularfeld des IdP und validiert sie gegen den gestarteten SSO-Vorgang, Providerkonfiguration und Signatur. Nutzt _edsso-Cookie oder die ausdrücklich konfigurierte cookiearme Korrelation. Kann den zugeordneten Administrator/Teilnehmer anlegen oder verbinden und eine Login-Sitzung erstellen; danach Browser-Weiterleitung. Kein JSON-data-Envelope und kein frei aufrufbarer API-Login. - Operation-ID: post_v2_auth_saml_reply - Dokumentation: /docs/api/v2/operations/post-v2-auth-saml-reply-f7b34370e447/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/saml/reply' \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/saml/reply"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/auth/saml/reply" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_saml_reply","summary":"SAML-Identitätsantwort empfangen","description":"Empfängt SAMLResponse als Formularfeld des IdP und validiert sie gegen den gestarteten SSO-Vorgang, Providerkonfiguration und Signatur. Nutzt _edsso-Cookie oder die ausdrücklich konfigurierte cookiearme Korrelation. Kann den zugeordneten Administrator/Teilnehmer anlegen oder verbinden und eine Login-Sitzung erstellen; danach Browser-Weiterleitung. Kein JSON-data-Envelope und kein frei aufrufbarer API-Login.","tags":["auth"],"security":[],"parameters":[],"responses":{"200":{"description":"Browser-Antwort/Weiterleitung nach SAML-Anmeldung; Fehler können ebenfalls als Login-Weiterleitung erscheinen.","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"Browser-Weiterleitung zum Folgeschritt oder zur Fehleransicht.","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"Vom Server ermittelte Zieladresse."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"SAMLResponse":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Base64-kodierte signierte SAMLResponse des IdP."}},"additionalProperties":true,"required":["SAMLResponse"]},"example":{"SAMLResponse":"c3ludGhldGljLXNhbWwtcmVzcG9uc2U="}}}},"x-edoobox-exampleStatus":"Requires a real signed SAML response from the configured identity provider; synthetic signatures are not executable examples.","x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/saml/sso/{accountIdentifier} SAML-Anmeldeanbieter eines Kontos auflisten Liest bis zu zehn aktive, nicht gelöschte SAML-Provider des über accountIdentifier gewählten Kontokürzels für Administrator-Anmeldungen (login_scope=both/admin). Gibt Organisation, öffentliche account-/saml-IDs und die serverseitig erzeugte Login-URL zurück. Öffentlicher Discovery-Schritt mit technischen Zugriffslimits; es wird noch keine Anmeldung erstellt. - Operation-ID: get_v2_auth_saml_sso_accountIdentifier - Dokumentation: /docs/api/v2/operations/get-v2-auth-saml-sso-accountidentifier-4131d8bd7155/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | accountIdentifier | path | ja | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"accountIdentifier","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Existing public account shortcut or source-supported account identifier."}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_ACCOUNTIDENTIFIER:?PATH_ACCOUNTIDENTIFIER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/saml/sso/'"${PATH_ACCOUNTIDENTIFIER}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/saml/sso/{accountIdentifier}"; path = path.replaceAll("{accountIdentifier}", encodeURIComponent(required("PATH_ACCOUNTIDENTIFIER"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/saml/sso/{accountIdentifier}" path = path.replace("{accountIdentifier}", urllib.parse.quote(os.environ["PATH_ACCOUNTIDENTIFIER"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_saml_sso_accountIdentifier","summary":"SAML-Anmeldeanbieter eines Kontos auflisten","description":"Liest bis zu zehn aktive, nicht gelöschte SAML-Provider des über accountIdentifier gewählten Kontokürzels für Administrator-Anmeldungen (login_scope=both/admin). Gibt Organisation, öffentliche account-/saml-IDs und die serverseitig erzeugte Login-URL zurück. Öffentlicher Discovery-Schritt mit technischen Zugriffslimits; es wird noch keine Anmeldung erstellt.","tags":["auth"],"security":[],"parameters":[{"name":"accountIdentifier","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Existing public account shortcut or source-supported account identifier."}}],"responses":{"200":{"description":"Aktive Administrator-SSO-Anbieter; kein Login-Token und keine internen IDs.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"organization_name":"Beispielakademie","account":"account_012345abcdef_123456","saml":"saml_012345abcdef_123456","provider_name":"Beispiel-IDP","url":"https://app2.edoobox.com/v2/auth/saml/redirect/saml_012345abcdef_123456/account_012345abcdef_123456/"}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"organization_name":{"type":"string"},"account":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"saml":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"provider_name":{"type":"string"},"url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["organization_name","account","saml","provider_name","url"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Samlfunc.php","method":"getListProviderAPI","line":1502},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/saml/sso/{accountIdentifier} SAML-Anmeldeanbieter eines Kontos auflisten Liest bis zu zehn aktive, nicht gelöschte SAML-Provider des über accountIdentifier gewählten Kontokürzels für Administrator-Anmeldungen (login_scope=both/admin). Gibt Organisation, öffentliche account-/saml-IDs und die serverseitig erzeugte Login-URL zurück. Öffentlicher Discovery-Schritt mit technischen Zugriffslimits; es wird noch keine Anmeldung erstellt. - Operation-ID: post_v2_auth_saml_sso_accountIdentifier - Dokumentation: /docs/api/v2/operations/post-v2-auth-saml-sso-accountidentifier-113c9252c086/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | accountIdentifier | path | ja | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"accountIdentifier","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Existing public account shortcut or source-supported account identifier."}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_ACCOUNTIDENTIFIER:?PATH_ACCOUNTIDENTIFIER setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/saml/sso/'"${PATH_ACCOUNTIDENTIFIER}"'' ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/saml/sso/{accountIdentifier}"; path = path.replaceAll("{accountIdentifier}", encodeURIComponent(required("PATH_ACCOUNTIDENTIFIER"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/saml/sso/{accountIdentifier}" path = path.replace("{accountIdentifier}", urllib.parse.quote(os.environ["PATH_ACCOUNTIDENTIFIER"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_saml_sso_accountIdentifier","summary":"SAML-Anmeldeanbieter eines Kontos auflisten","description":"Liest bis zu zehn aktive, nicht gelöschte SAML-Provider des über accountIdentifier gewählten Kontokürzels für Administrator-Anmeldungen (login_scope=both/admin). Gibt Organisation, öffentliche account-/saml-IDs und die serverseitig erzeugte Login-URL zurück. Öffentlicher Discovery-Schritt mit technischen Zugriffslimits; es wird noch keine Anmeldung erstellt.","tags":["auth"],"security":[],"parameters":[{"name":"accountIdentifier","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Existing public account shortcut or source-supported account identifier."}}],"responses":{"200":{"description":"Aktive Administrator-SSO-Anbieter; kein Login-Token und keine internen IDs.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"organization_name":"Beispielakademie","account":"account_012345abcdef_123456","saml":"saml_012345abcdef_123456","provider_name":"Beispiel-IDP","url":"https://app2.edoobox.com/v2/auth/saml/redirect/saml_012345abcdef_123456/account_012345abcdef_123456/"}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"organization_name":{"type":"string"},"account":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"saml":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"provider_name":{"type":"string"},"url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["organization_name","account","saml","provider_name","url"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-bodylessSource":{"file":"application/models/v2/Samlfunc.php","method":"getListProviderAPI","line":1502},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Samlfunc.php","method":"getListProviderAPI","line":1502},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # PUT /v2/auth/skip/skip_counter Zahlungshinweis einmal überspringen Verbraucht, sofern vorhanden, einen verbleibenden Überspring-Schritt des gewählten Kontos und protokolliert den Administrator. Der Konto-Kontext stammt aus der authentifizierten Sitzung; kein frei wählbares Fremdkonto. Der Dispatcher verlangt einen id-Selektor, auch beim /list-Alias; /skip liefert ihn über das Pfadsegment. - Operation-ID: put_v2_auth_skip_skip_counter - Dokumentation: /docs/api/v2/operations/put-v2-auth-skip-skip-counter-e7dd42046222/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/auth/skip/skip_counter' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/skip/skip_counter"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/skip/skip_counter" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_auth_skip_skip_counter","summary":"Zahlungshinweis einmal überspringen","description":"Verbraucht, sofern vorhanden, einen verbleibenden Überspring-Schritt des gewählten Kontos und protokolliert den Administrator. Der Konto-Kontext stammt aus der authentifizierten Sitzung; kein frei wählbares Fremdkonto. Der Dispatcher verlangt einen id-Selektor, auch beim /list-Alias; /skip liefert ihn über das Pfadsegment.","tags":["auth"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Konto-ID, update und verbleibender skip_counter; bei tatsächlichem Verbrauch zusätzlich öffentliche skip-ID und opaker token.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":false,"skip_counter":0}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"update":{"type":"boolean"},"skip_counter":{"type":"integer"},"skip":{"type":"string","pattern":"^skip_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"token":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."}},"additionalProperties":true,"required":["id","update","skip_counter"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Auth.php","line":189,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-bodylessSource":{"file":"application/models/v2/Edsubscriptionfunc.php","line":295},"x-edoobox-account-mutation":true,"x-edoobox-account-mutation-source":{"file":"application/models/v2/Edsubscriptionfunc.php","line":295,"reason":"Decrements the authenticated account billing-skip counter and writes an admin log. No session/token replacement."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"additionalProperties":true},"examples":{"request":{"summary":"Zahlungshinweis einmal überspringen","value":{}}}}},"required":false},"x-edoobox-source":{"file":"application/models/v2/Auth.php","method":"updateAuth","line":194,"branch":{"file":"application/models/v2/Auth.php","line":206}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/auth/system-feedback-reports Öffentlichen Systemfehlerbericht speichern Nimmt einen nicht leeren data-Bericht aus der öffentlichen Anwendung entgegen, begrenzt Wiederholungen und speichert bereinigte Route, Kontext, Telemetrie und optionale Artefakte für Supportauswertung. Account-Bezug darf nur als öffentliche accountId/accountEdid übergeben werden. Rate-Limit kann mit HTTP 200 und stored=false/status=ignored antworten; es wird keine interaktive Fehlermeldung versendet. - Operation-ID: post_v2_auth_system_feedback_reports - Dokumentation: /docs/api/v2/operations/post-v2-auth-system-feedback-reports-ce5db1f1c196/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "payloadVersion": 1, "trigger": "system", "feedbackType": "bug", "comment": "Die Kalenderansicht konnte nicht geladen werden.", "route": { "name": "calendar", "path": "/apps/calendar" }, "telemetry": { "viewport": { "width": 1280, "height": 720 } } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/system-feedback-reports' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/system-feedback-reports"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "payloadVersion": 1, "trigger": "system", "feedbackType": "bug", "comment": "Die Kalenderansicht konnte nicht geladen werden.", "route": { "name": "calendar", "path": "/apps/calendar" }, "telemetry": { "viewport": { "width": 1280, "height": 720 } } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/system-feedback-reports" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"data\":{\"payloadVersion\":1,\"trigger\":\"system\",\"feedbackType\":\"bug\",\"comment\":\"Die Kalenderansicht konnte nicht geladen werden.\",\"route\":{\"name\":\"calendar\",\"path\":\"/apps/calendar\"},\"telemetry\":{\"viewport\":{\"width\":1280,\"height\":720}}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_system_feedback_reports","summary":"Öffentlichen Systemfehlerbericht speichern","description":"Nimmt einen nicht leeren data-Bericht aus der öffentlichen Anwendung entgegen, begrenzt Wiederholungen und speichert bereinigte Route, Kontext, Telemetrie und optionale Artefakte für Supportauswertung. Account-Bezug darf nur als öffentliche accountId/accountEdid übergeben werden. Rate-Limit kann mit HTTP 200 und stored=false/status=ignored antworten; es wird keine interaktive Fehlermeldung versendet.","tags":["auth"],"security":[],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Berichtsstatus steht auf oberster Ebene: stored/ignored, stored, rateLimited und rateLimit; bei Speicherung zusätzlich öffentliche reportId, receivedAt und Artefakt-Referenzen.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":["boolean","string"]},"status":{"type":"string"},"stored":{"type":"boolean"},"rateLimited":{"type":"boolean"},"reportId":{"type":"string"},"receivedAt":{"type":"string"},"rateLimit":{"type":"object","additionalProperties":true},"artifacts":{"type":"array","items":{"type":"object","additionalProperties":true}}},"additionalProperties":true},"example":{"error":false,"status":"stored","stored":true,"rateLimited":false,"reportId":"efb_synthetic_report","receivedAt":"2026-10-22T12:00:00+00:00","rateLimit":{},"artifacts":[]}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"payloadVersion":{"description":"Versionswert wird numerisch auf 1–99 begrenzt; kanonisch Integer.","examples":[1]},"trigger":{"description":"Kanonisch Text; Standard system.","examples":["system"]},"feedbackType":{"description":"Berichtstyp wird vom nativen Normalisierer geprüft; Standard bug.","examples":["bug"]},"comment":{"description":"Optionaler Berichtstext, wird bereinigt und gekürzt.","examples":["Die Kalenderansicht konnte nicht geladen werden."]},"route":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"fullPath":{"type":"string"}},"additionalProperties":true},"appContext":{"type":"object","additionalProperties":true},"telemetry":{"type":"object","additionalProperties":true},"annotations":{},"accountId":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"accountEdid":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true,"minProperties":1}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Öffentlichen Systemfehlerbericht speichern","value":{"data":{"payloadVersion":1,"trigger":"system","feedbackType":"bug","comment":"Die Kalenderansicht konnte nicht geladen werden.","route":{"name":"calendar","path":"/apps/calendar"},"telemetry":{"viewport":{"width":1280,"height":720}}}}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"handlePublicSystemFeedback","line":535},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth","short":"auth","checkedByDispatcher":false,"levels":[],"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme."}} ``` --- # GET /v2/auth/trainer-course Formularauswahl und eigene Trainerangebote lesen Liest Trainername, Kontoformate, Kategorien der zweiten Stammstruktur, Tags, aktive Orte/Räume sowie höchstens 200 vom Trainer erstellte Angebote. Der persönliche Bearer-Code ersetzt den gewöhnlichen Kontokontext. Die Antwort ist vertraulich und wird mit no-store/private und no-referrer ausgeliefert. Ein ungültiger Code erhöht den IP-Fehlversuchszähler; nach fünf Fehlversuchen innerhalb einer Stunde wird die IP eine Stunde gesperrt. Gültige Codes setzen frühere Fehlversuche nicht zurück. - Operation-ID: get_v2_auth_trainer_course - Dokumentation: /docs/api/v2/operations/get-v2-auth-trainer-course-03830f9768a1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **trainerToken** (HTTP bearer) — Dedicated revocable trainer-course token; not a normal account JWT. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"trainerToken":[]}],"securitySchemes":{"trainerToken":{"type":"http","scheme":"bearer","description":"Dedicated revocable trainer-course token; not a normal account JWT."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | Authorization | header | ja | Persönlicher Trainer-Bearer-Code aus der administrativ verwalteten Freigabe; kein gewöhnlicher V2-Admin-Token. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"Authorization","in":"header","required":true,"schema":{"type":"string","pattern":"^Bearer [A-Za-z0-9_-]{43}$"},"description":"Persönlicher Trainer-Bearer-Code aus der administrativ verwalteten Freigabe; kein gewöhnlicher V2-Admin-Token."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_TRAINERTOKEN:?AUTH_TRAINERTOKEN setzen}" : "${HEADER_AUTHORIZATION:?HEADER_AUTHORIZATION setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/trainer-course' \ --header 'Authorization: Bearer '"${AUTH_TRAINERTOKEN}" ``` ### JavaScript (Node.js) ```javascript const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/trainer-course"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_TRAINERTOKEN"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/trainer-course" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_TRAINERTOKEN"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_trainer_course","summary":"Formularauswahl und eigene Trainerangebote lesen","description":"Liest Trainername, Kontoformate, Kategorien der zweiten Stammstruktur, Tags, aktive Orte/Räume sowie höchstens 200 vom Trainer erstellte Angebote. Der persönliche Bearer-Code ersetzt den gewöhnlichen Kontokontext. Die Antwort ist vertraulich und wird mit no-store/private und no-referrer ausgeliefert. Ein ungültiger Code erhöht den IP-Fehlversuchszähler; nach fünf Fehlversuchen innerhalb einer Stunde wird die IP eine Stunde gesperrt. Gültige Codes setzen frühere Fehlversuche nicht zurück.","tags":["trainer-course"],"security":[{"trainerToken":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"Authorization","in":"header","required":true,"schema":{"type":"string","pattern":"^Bearer [A-Za-z0-9_-]{43}$"},"description":"Persönlicher Trainer-Bearer-Code aus der administrativ verwalteten Freigabe; kein gewöhnlicher V2-Admin-Token."}],"responses":{"200":{"description":"data enthält settings, trainer sowie Auswahl- und Angebotslisten. Alle IDs verwenden das jeweilige öffentliche v2-Format.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"settings":{"format_date":0,"format_time":0,"firstweekday":1,"format_timezone":"Europe/Zurich","language":"de"},"trainer":{"name":"Beispiel Trainer"},"categories":[{"id":"category_012345abcdef_123456","name":"Kurse"}],"tags":[],"places":[],"rooms":[],"courses":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"settings":{"type":"object","properties":{"format_date":{"type":"integer"},"format_time":{"type":"integer"},"firstweekday":{"type":"integer"},"format_timezone":{"type":"string"},"language":{"type":"string"}},"additionalProperties":true},"trainer":{"type":"object","properties":{"name":{"type":"string"}},"additionalProperties":true},"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"tags":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"places":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},"place":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"courses":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"name":{"type":"string"},"number":{"type":"string"},"date_start":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"status":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Trainercourse.php","method":"handlePublicRequest","line":88},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"trainer-course","short":null,"checkedByDispatcher":false,"levels":[],"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme."}} ``` --- # POST /v2/auth/trainer-course Trainer-Kursentwurf anlegen oder Datei hochladen Verarbeitet action=create_course (Standard), prepare_title_image, upload_title_image oder upload_external_file. create_course erstellt einen unveröffentlichten Kurs status=0 mit optionalem Termin, Preis, Texten, interner Notiz und Titelbild. prepare_title_image registriert Titelbild-Metadaten und gibt eine signierte Upload-URL zurück. Multipart-Aktionen speichern eine Datei an einem eigenen noch unveröffentlichten Entwurf. Kategorien/Orte/Räume/Tags müssen zur Auswahl gehören, Titelbilder dem Trainer zugeordnet sein. Der persönliche Trainer-Bearer-Code ist erforderlich. Antworten und Upload-URLs sind vertraulich; fünf ungültige Codes innerhalb einer Stunde sperren die IP für eine Stunde. minimum darf maximum nicht übersteigen. Bei gesetztem start oder end sind beide gültigen lokalen Datumswerte erforderlich, end muss nach start liegen. Bei Multipart stehen action und offer direkt neben file, ohne data-JSON. Titelbilder erlauben JPEG/PNG bis 2 MiB und 40 Millionen Pixel, externe Dateien PDF/JPEG/PNG/WebP bis 20 MiB und höchstens zehn je Kurs. title muss nach der Textbereinigung einen nicht leeren Titel ergeben. - Operation-ID: post_v2_auth_trainer_course - Dokumentation: /docs/api/v2/operations/post-v2-auth-trainer-course-d982b815ab77/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **trainerToken** (HTTP bearer) — Dedicated revocable trainer-course token; not a normal account JWT. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"trainerToken":[]}],"securitySchemes":{"trainerToken":{"type":"http","scheme":"bearer","description":"Dedicated revocable trainer-course token; not a normal account JWT."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | Authorization | header | ja | Persönlicher Trainer-Bearer-Code aus der administrativ verwalteten Freigabe; kein gewöhnlicher V2-Admin-Token. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"Authorization","in":"header","required":true,"schema":{"type":"string","pattern":"^Bearer [A-Za-z0-9_-]{43}$"},"description":"Persönlicher Trainer-Bearer-Code aus der administrativ verwalteten Freigabe; kein gewöhnlicher V2-Admin-Token."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_TRAINERTOKEN:?AUTH_TRAINERTOKEN setzen}" : "${HEADER_AUTHORIZATION:?HEADER_AUTHORIZATION setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "action": "create_course", "title": "Einführungskurs", "category": "category_012345abcdef_123456", "number": "K-2027-01", "minimum": 1, "maximum": 12, "price": 120, "start": "2027-04-01T09:00", "end": "2027-04-01T12:00", "tags": [] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/trainer-course' \ --header 'Authorization: Bearer '"${AUTH_TRAINERTOKEN}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/trainer-course"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_TRAINERTOKEN"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "action": "create_course", "title": "Einführungskurs", "category": "category_012345abcdef_123456", "number": "K-2027-01", "minimum": 1, "maximum": 12, "price": 120, "start": "2027-04-01T09:00", "end": "2027-04-01T12:00", "tags": [] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/trainer-course" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_TRAINERTOKEN"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"action\":\"create_course\",\"title\":\"Einführungskurs\",\"category\":\"category_012345abcdef_123456\",\"number\":\"K-2027-01\",\"minimum\":1,\"maximum\":12,\"price\":120,\"start\":\"2027-04-01T09:00\",\"end\":\"2027-04-01T12:00\",\"tags\":[]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_trainer_course","summary":"Trainer-Kursentwurf anlegen oder Datei hochladen","description":"Verarbeitet action=create_course (Standard), prepare_title_image, upload_title_image oder upload_external_file. create_course erstellt einen unveröffentlichten Kurs status=0 mit optionalem Termin, Preis, Texten, interner Notiz und Titelbild. prepare_title_image registriert Titelbild-Metadaten und gibt eine signierte Upload-URL zurück. Multipart-Aktionen speichern eine Datei an einem eigenen noch unveröffentlichten Entwurf. Kategorien/Orte/Räume/Tags müssen zur Auswahl gehören, Titelbilder dem Trainer zugeordnet sein. Der persönliche Trainer-Bearer-Code ist erforderlich. Antworten und Upload-URLs sind vertraulich; fünf ungültige Codes innerhalb einer Stunde sperren die IP für eine Stunde. minimum darf maximum nicht übersteigen. Bei gesetztem start oder end sind beide gültigen lokalen Datumswerte erforderlich, end muss nach start liegen. Bei Multipart stehen action und offer direkt neben file, ohne data-JSON. Titelbilder erlauben JPEG/PNG bis 2 MiB und 40 Millionen Pixel, externe Dateien PDF/JPEG/PNG/WebP bis 20 MiB und höchstens zehn je Kurs. title muss nach der Textbereinigung einen nicht leeren Titel ergeben.","tags":["trainer-course"],"security":[{"trainerToken":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"Authorization","in":"header","required":true,"schema":{"type":"string","pattern":"^Bearer [A-Za-z0-9_-]{43}$"},"description":"Persönlicher Trainer-Bearer-Code aus der administrativ verwalteten Freigabe; kein gewöhnlicher V2-Admin-Token."}],"responses":{"200":{"description":"create_course liefert offer-ID/created/status. prepare_title_image liefert publicfile-ID/insert/upload_url/cdn_url. upload_title_image liefert offer-ID/update/image/files/url; upload_external_file liefert files-ID/insert.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","created":true,"status":0}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"created":{"const":true},"status":{"const":0}},"additionalProperties":true,"required":["id","created","status"]},{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"insert":{"const":true},"upload_url":{"type":"string","format":"uri","description":"Vertrauliche signierte Upload-URL."},"cdn_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["id","insert","upload_url","cdn_url"]},{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"const":true},"image":{"type":"string"},"files":{"type":"array","items":{"type":"string"}},"url":{"type":"string","description":"Öffentliche Bild-URL."}},"additionalProperties":true,"required":["id","update","image","files","url"]},{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"insert":{"const":true}},"additionalProperties":true,"required":["id","insert"]}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"action":{"const":"create_course","default":"create_course"},"title":{"type":["string","number","boolean"],"description":"Skalarer Text; vor Normalisierung höchstens 80 Zeichen. HTML/Leerzeichen werden bereinigt."},"number":{"type":["string","number","boolean"],"description":"Skalarer Text; vor Normalisierung höchstens 120 Zeichen. HTML/Leerzeichen werden bereinigt."},"category":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"description":{"type":["string","number","boolean"],"description":"Skalarer Text; vor Normalisierung höchstens 50000 Zeichen. HTML/Leerzeichen werden bereinigt."},"description_html":{"description":"PHP-empty/Wahrheitsvergleich steuert die HTML-Verarbeitung.","examples":[true]},"booking_message":{"description":"Skalarer Text, auf 200000 Zeichen normalisiert; bei HTML-Flag String mit höchstens 50000 Zeichen.","examples":["Bitte pünktlich erscheinen."]},"booking_message_html":{"description":"PHP-empty/Wahrheitsvergleich steuert die HTML-Verarbeitung.","examples":[true]},"internal_note":{"description":"Interne Notiz; skalarer Text wird auf 1000 Zeichen normalisiert."},"minimum":{"type":["number","string","null","boolean"],"description":"Leertext/null/false gelten als nicht gesetzt; sonst per FILTER_VALIDATE_INT validierte Ganzzahl 0–99999."},"maximum":{"type":["number","string","null","boolean"],"description":"Leertext/null/false gelten als nicht gesetzt; sonst per FILTER_VALIDATE_INT validierte Ganzzahl 0–99999."},"price":{"type":["number","string","null","boolean"],"description":"Leer/null/false ohne Preis; sonst nichtnegative endliche Zahl bzw. numerischer Text bis 9999999.99, auf zwei Dezimalstellen gerundet."},"start":{"type":["string","null","boolean"],"description":"YYYY-MM-DDTHH:mm in Kontozeitzone, ohne Sekunden/Offset. Leer/null/false ohne Termin; sobald ein Datum gesetzt ist, sind beide erforderlich."},"end":{"type":["string","null","boolean"],"description":"YYYY-MM-DDTHH:mm in Kontozeitzone, ohne Sekunden/Offset. Leer/null/false ohne Termin; sobald ein Datum gesetzt ist, sind beide erforderlich."},"place":{"anyOf":[{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},{"enum":["",null,false]}]},"room":{"anyOf":[{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},{"enum":["",null,false]}]},"tags":{"type":"array","items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"maxItems":50},"title_image":{"anyOf":[{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},{"enum":["",null,false]}]}},"additionalProperties":true,"required":["title","category"]},{"type":"object","properties":{"action":{"const":"prepare_title_image"},"file_name":{"type":["string","number","boolean"],"description":"Dateiname mit jpg/jpeg/png-Endung; wird als Text auf 180 Zeichen normalisiert."},"file_size":{"type":["number","string","boolean"],"description":"Per FILTER_VALIDATE_INT geprüfte positive Ganzzahl bis 2097152 Bytes."},"file_type":{"type":"string","description":"Nach Kleinschreibung/Trimmen image/jpeg oder image/png; Endung muss dazu passen.","examples":["image/png"]}},"additionalProperties":true,"required":["action","file_name","file_size","file_type"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"create_course":{"value":{"data":{"action":"create_course","title":"Einführungskurs","category":"category_012345abcdef_123456","number":"K-2027-01","minimum":1,"maximum":12,"price":120,"start":"2027-04-01T09:00","end":"2027-04-01T12:00","tags":[]}}},"prepare_title_image":{"value":{"data":{"action":"prepare_title_image","file_name":"titelbild.png","file_size":1024,"file_type":"image/png"}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["upload_title_image","upload_external_file"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"file":{"type":"string","format":"binary","description":"Titelbild: JPEG/PNG bis 2 MiB und höchstens 40 Millionen Pixel. Externe Datei: PDF/JPEG/PNG/WebP bis 20 MiB, höchstens zehn je Kurs."}},"additionalProperties":true,"required":["action","offer","file"]}}}},"x-edoobox-authentication-lifecycle":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Trainercourse.php","method":"handlePublicRequest","line":88},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"trainer-course","short":null,"checkedByDispatcher":false,"levels":[],"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme."}} ``` --- # GET /v2/auth/webhook/paypal Signierte PayPal-Ereignisse verarbeiten Empfängt den originalen PayPal-JSON-Body und prüft Provider-Signatur samt Übertragungsheadern, bevor Zahlungen, Rückerstattungen oder Händlerverbindungsstatus verarbeitet werden. Die Antwort wird früh bestätigt; ihr Erfolg beweist keine fachliche Verarbeitung. Der Legacy-GET-Alias nutzt denselben Body-Handler und kann bei gültigem Provider-Ereignis Daten verändern. Kein data-Envelope um das Provider-Event legen. - Operation-ID: get_v2_auth_webhook_paypal - Dokumentation: /docs/api/v2/operations/get-v2-auth-webhook-paypal-c3d16760c59a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | PAYPAL-CERT-URL | header | ja | Originaler PayPal-Verifikationsheader. | | PAYPAL-TRANSMISSION-ID | header | ja | Originaler PayPal-Verifikationsheader. | | PAYPAL-TRANSMISSION-TIME | header | ja | Originaler PayPal-Verifikationsheader. | | PAYPAL-TRANSMISSION-SIG | header | ja | Originaler PayPal-Verifikationsheader. | | PAYPAL-AUTH-ALGO | header | ja | Originaler PayPal-Verifikationsheader. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"PAYPAL-CERT-URL","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-ID","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-TIME","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-SIG","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-AUTH-ALGO","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${HEADER_PAYPAL_CERT_URL:?HEADER_PAYPAL_CERT_URL setzen}" : "${HEADER_PAYPAL_TRANSMISSION_ID:?HEADER_PAYPAL_TRANSMISSION_ID setzen}" : "${HEADER_PAYPAL_TRANSMISSION_TIME:?HEADER_PAYPAL_TRANSMISSION_TIME setzen}" : "${HEADER_PAYPAL_TRANSMISSION_SIG:?HEADER_PAYPAL_TRANSMISSION_SIG setzen}" : "${HEADER_PAYPAL_AUTH_ALGO:?HEADER_PAYPAL_AUTH_ALGO setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "WH-SYNTHETIC-EVENT", "event_type": "MERCHANT.ONBOARDING.COMPLETED", "resource": { "tracking_id": "account_012345abcdef_123456", "merchant_id": "SYNTHETIC-MERCHANT" } } EDOOBOX_JSON_EXAMPLE curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/webhook/paypal' \ --header 'PAYPAL-CERT-URL: '"${HEADER_PAYPAL_CERT_URL}" \ --header 'PAYPAL-TRANSMISSION-ID: '"${HEADER_PAYPAL_TRANSMISSION_ID}" \ --header 'PAYPAL-TRANSMISSION-TIME: '"${HEADER_PAYPAL_TRANSMISSION_TIME}" \ --header 'PAYPAL-TRANSMISSION-SIG: '"${HEADER_PAYPAL_TRANSMISSION_SIG}" \ --header 'PAYPAL-AUTH-ALGO: '"${HEADER_PAYPAL_AUTH_ALGO}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Fetch unterstützt GET mit Request Body nicht. // Verwende das cURL- oder Python-Beispiel oder einen HTTP-Client mit Unterstützung dieser Methode. throw new Error("Für GET mit Request Body ist kein gültiges Fetch-Beispiel verfügbar."); ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/webhook/paypal" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["PAYPAL-CERT-URL"] = "" + os.environ["HEADER_PAYPAL_CERT_URL"] headers["PAYPAL-TRANSMISSION-ID"] = "" + os.environ["HEADER_PAYPAL_TRANSMISSION_ID"] headers["PAYPAL-TRANSMISSION-TIME"] = "" + os.environ["HEADER_PAYPAL_TRANSMISSION_TIME"] headers["PAYPAL-TRANSMISSION-SIG"] = "" + os.environ["HEADER_PAYPAL_TRANSMISSION_SIG"] headers["PAYPAL-AUTH-ALGO"] = "" + os.environ["HEADER_PAYPAL_AUTH_ALGO"] headers["Content-Type"] = "application/json" body = "{\"id\":\"WH-SYNTHETIC-EVENT\",\"event_type\":\"MERCHANT.ONBOARDING.COMPLETED\",\"resource\":{\"tracking_id\":\"account_012345abcdef_123456\",\"merchant_id\":\"SYNTHETIC-MERCHANT\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_webhook_paypal","summary":"Signierte PayPal-Ereignisse verarbeiten","description":"Empfängt den originalen PayPal-JSON-Body und prüft Provider-Signatur samt Übertragungsheadern, bevor Zahlungen, Rückerstattungen oder Händlerverbindungsstatus verarbeitet werden. Die Antwort wird früh bestätigt; ihr Erfolg beweist keine fachliche Verarbeitung. Der Legacy-GET-Alias nutzt denselben Body-Handler und kann bei gültigem Provider-Ereignis Daten verändern. Kein data-Envelope um das Provider-Event legen.","tags":["auth"],"security":[],"parameters":[{"name":"PAYPAL-CERT-URL","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-ID","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-TIME","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-SIG","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-AUTH-ALGO","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."}],"responses":{"200":{"description":"Frühe Textbestätigung success; tatsächliche Verarbeitung erfolgt anschließend.","content":{"text/html":{"schema":{"type":"string"},"example":"success"}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"event_type":{"type":"string"},"resource":{"type":"object","additionalProperties":true}},"additionalProperties":true,"required":["id","event_type","resource"]},"examples":{"request":{"summary":"Signierte PayPal-Ereignisse verarbeiten","value":{"id":"WH-SYNTHETIC-EVENT","event_type":"MERCHANT.ONBOARDING.COMPLETED","resource":{"tracking_id":"account_012345abcdef_123456","merchant_id":"SYNTHETIC-MERCHANT"}}}}}},"required":true},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/webhook/paypal Signierte PayPal-Ereignisse verarbeiten Empfängt den originalen PayPal-JSON-Body und prüft Provider-Signatur samt Übertragungsheadern, bevor Zahlungen, Rückerstattungen oder Händlerverbindungsstatus verarbeitet werden. Die Antwort wird früh bestätigt; ihr Erfolg beweist keine fachliche Verarbeitung. Der Legacy-GET-Alias nutzt denselben Body-Handler und kann bei gültigem Provider-Ereignis Daten verändern. Kein data-Envelope um das Provider-Event legen. - Operation-ID: post_v2_auth_webhook_paypal - Dokumentation: /docs/api/v2/operations/post-v2-auth-webhook-paypal-d7d9fb23bd07/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | PAYPAL-CERT-URL | header | ja | Originaler PayPal-Verifikationsheader. | | PAYPAL-TRANSMISSION-ID | header | ja | Originaler PayPal-Verifikationsheader. | | PAYPAL-TRANSMISSION-TIME | header | ja | Originaler PayPal-Verifikationsheader. | | PAYPAL-TRANSMISSION-SIG | header | ja | Originaler PayPal-Verifikationsheader. | | PAYPAL-AUTH-ALGO | header | ja | Originaler PayPal-Verifikationsheader. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"PAYPAL-CERT-URL","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-ID","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-TIME","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-SIG","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-AUTH-ALGO","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${HEADER_PAYPAL_CERT_URL:?HEADER_PAYPAL_CERT_URL setzen}" : "${HEADER_PAYPAL_TRANSMISSION_ID:?HEADER_PAYPAL_TRANSMISSION_ID setzen}" : "${HEADER_PAYPAL_TRANSMISSION_TIME:?HEADER_PAYPAL_TRANSMISSION_TIME setzen}" : "${HEADER_PAYPAL_TRANSMISSION_SIG:?HEADER_PAYPAL_TRANSMISSION_SIG setzen}" : "${HEADER_PAYPAL_AUTH_ALGO:?HEADER_PAYPAL_AUTH_ALGO setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "WH-SYNTHETIC-EVENT", "event_type": "MERCHANT.ONBOARDING.COMPLETED", "resource": { "tracking_id": "account_012345abcdef_123456", "merchant_id": "SYNTHETIC-MERCHANT" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/webhook/paypal' \ --header 'PAYPAL-CERT-URL: '"${HEADER_PAYPAL_CERT_URL}" \ --header 'PAYPAL-TRANSMISSION-ID: '"${HEADER_PAYPAL_TRANSMISSION_ID}" \ --header 'PAYPAL-TRANSMISSION-TIME: '"${HEADER_PAYPAL_TRANSMISSION_TIME}" \ --header 'PAYPAL-TRANSMISSION-SIG: '"${HEADER_PAYPAL_TRANSMISSION_SIG}" \ --header 'PAYPAL-AUTH-ALGO: '"${HEADER_PAYPAL_AUTH_ALGO}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/webhook/paypal"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["PAYPAL-CERT-URL"] = "" + required("HEADER_PAYPAL_CERT_URL"); headers["PAYPAL-TRANSMISSION-ID"] = "" + required("HEADER_PAYPAL_TRANSMISSION_ID"); headers["PAYPAL-TRANSMISSION-TIME"] = "" + required("HEADER_PAYPAL_TRANSMISSION_TIME"); headers["PAYPAL-TRANSMISSION-SIG"] = "" + required("HEADER_PAYPAL_TRANSMISSION_SIG"); headers["PAYPAL-AUTH-ALGO"] = "" + required("HEADER_PAYPAL_AUTH_ALGO"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "WH-SYNTHETIC-EVENT", "event_type": "MERCHANT.ONBOARDING.COMPLETED", "resource": { "tracking_id": "account_012345abcdef_123456", "merchant_id": "SYNTHETIC-MERCHANT" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/webhook/paypal" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["PAYPAL-CERT-URL"] = "" + os.environ["HEADER_PAYPAL_CERT_URL"] headers["PAYPAL-TRANSMISSION-ID"] = "" + os.environ["HEADER_PAYPAL_TRANSMISSION_ID"] headers["PAYPAL-TRANSMISSION-TIME"] = "" + os.environ["HEADER_PAYPAL_TRANSMISSION_TIME"] headers["PAYPAL-TRANSMISSION-SIG"] = "" + os.environ["HEADER_PAYPAL_TRANSMISSION_SIG"] headers["PAYPAL-AUTH-ALGO"] = "" + os.environ["HEADER_PAYPAL_AUTH_ALGO"] headers["Content-Type"] = "application/json" body = "{\"id\":\"WH-SYNTHETIC-EVENT\",\"event_type\":\"MERCHANT.ONBOARDING.COMPLETED\",\"resource\":{\"tracking_id\":\"account_012345abcdef_123456\",\"merchant_id\":\"SYNTHETIC-MERCHANT\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_webhook_paypal","summary":"Signierte PayPal-Ereignisse verarbeiten","description":"Empfängt den originalen PayPal-JSON-Body und prüft Provider-Signatur samt Übertragungsheadern, bevor Zahlungen, Rückerstattungen oder Händlerverbindungsstatus verarbeitet werden. Die Antwort wird früh bestätigt; ihr Erfolg beweist keine fachliche Verarbeitung. Der Legacy-GET-Alias nutzt denselben Body-Handler und kann bei gültigem Provider-Ereignis Daten verändern. Kein data-Envelope um das Provider-Event legen.","tags":["auth"],"security":[],"parameters":[{"name":"PAYPAL-CERT-URL","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-ID","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-TIME","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-TRANSMISSION-SIG","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."},{"name":"PAYPAL-AUTH-ALGO","in":"header","required":true,"schema":{"type":"string"},"description":"Originaler PayPal-Verifikationsheader."}],"responses":{"200":{"description":"Frühe Textbestätigung success; tatsächliche Verarbeitung erfolgt anschließend.","content":{"text/html":{"schema":{"type":"string"},"example":"success"}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"event_type":{"type":"string"},"resource":{"type":"object","additionalProperties":true}},"additionalProperties":true,"required":["id","event_type","resource"]},"examples":{"request":{"summary":"Signierte PayPal-Ereignisse verarbeiten","value":{"id":"WH-SYNTHETIC-EVENT","event_type":"MERCHANT.ONBOARDING.COMPLETED","resource":{"tracking_id":"account_012345abcdef_123456","merchant_id":"SYNTHETIC-MERCHANT"}}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth/webhook/pingen/{token} Pingen-Briefstatus und Kosten übernehmen Empfängt einen Pingen-Rückruf. Das Pfadsegment token ist hier tatsächlich der Status-/Ereignistyp (z. B. send, error, not_deliverable), kein Authentifizierungstoken. data.relationships.letter.data.id identifiziert den externen Brief. Lädt dessen Status bei Pingen, aktualisiert lokalen Briefstatus und kann bei Rechnungen/Mahnungen Portokosten buchen. Der GET-Alias führt denselben schreibenden Handler aus; nicht als Leseabfrage verwenden. GET liest die verschachtelte Query-data-Struktur, POST den JSON-Body; ein GET-JSON-Body wird vom Dispatcher nicht übernommen. - Operation-ID: get_v2_auth_webhook_pingen_token - Dokumentation: /docs/api/v2/operations/get-v2-auth-webhook-pingen-token-a8ff0a3ee761/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | token | path | ja | Legacy-Name token im Pfad. | | data | query | ja | GET-Legacy-Alias erwartet verschachtelte Query-Parameter, etwa data[relationships][letter][data][id]; ein JSON-Body wird hier nicht gelesen. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"token","in":"path","required":true,"description":"Legacy-Name token im Pfad.","schema":{"type":"string","description":"Status-/Ereignistyp des Pingen-Callbacks; kein Schlüssel.","examples":["send"]}},{"name":"data","in":"query","required":true,"style":"deepObject","explode":true,"schema":{"type":"object","properties":{"relationships":{"type":"object","properties":{"letter":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"additionalProperties":true,"required":["id"]}},"additionalProperties":true,"required":["data"]}},"additionalProperties":true,"required":["letter"]}},"additionalProperties":true,"required":["relationships"]},"example":{"relationships":{"letter":{"data":{"id":"synthetic-external-letter-id"}}}},"description":"GET-Legacy-Alias erwartet verschachtelte Query-Parameter, etwa data[relationships][letter][data][id]; ein JSON-Body wird hier nicht gelesen."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # data: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_TOKEN:?PATH_TOKEN setzen}" : "${QUERY_DATA:?QUERY_DATA setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth/webhook/pingen/'"${PATH_TOKEN}"'' \ --url-query 'data='"${QUERY_DATA}" ``` ### JavaScript (Node.js) ```javascript // data: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/webhook/pingen/{token}"; path = path.replaceAll("{token}", encodeURIComponent(required("PATH_TOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("data", required("QUERY_DATA")); const headers = {}; const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # data: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/webhook/pingen/{token}" path = path.replace("{token}", urllib.parse.quote(os.environ["PATH_TOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("data", os.environ["QUERY_DATA"])]) headers = {} # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth_webhook_pingen_token","summary":"Pingen-Briefstatus und Kosten übernehmen","description":"Empfängt einen Pingen-Rückruf. Das Pfadsegment token ist hier tatsächlich der Status-/Ereignistyp (z. B. send, error, not_deliverable), kein Authentifizierungstoken. data.relationships.letter.data.id identifiziert den externen Brief. Lädt dessen Status bei Pingen, aktualisiert lokalen Briefstatus und kann bei Rechnungen/Mahnungen Portokosten buchen. Der GET-Alias führt denselben schreibenden Handler aus; nicht als Leseabfrage verwenden. GET liest die verschachtelte Query-data-Struktur, POST den JSON-Body; ein GET-JSON-Body wird vom Dispatcher nicht übernommen.","tags":["auth"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"Legacy-Name token im Pfad.","schema":{"type":"string","description":"Status-/Ereignistyp des Pingen-Callbacks; kein Schlüssel.","examples":["send"]}},{"name":"data","in":"query","required":true,"style":"deepObject","explode":true,"schema":{"type":"object","properties":{"relationships":{"type":"object","properties":{"letter":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"additionalProperties":true,"required":["id"]}},"additionalProperties":true,"required":["data"]}},"additionalProperties":true,"required":["letter"]}},"additionalProperties":true,"required":["relationships"]},"example":{"relationships":{"letter":{"data":{"id":"synthetic-external-letter-id"}}}},"description":"GET-Legacy-Alias erwartet verschachtelte Query-Parameter, etwa data[relationships][letter][data][id]; ein JSON-Body wird hier nicht gelesen."}],"responses":{"200":{"description":"Text OK nach Verarbeitung des Rückrufs.","content":{"text/plain":{"schema":{"type":"string"},"example":"OK"}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # POST /v2/auth/webhook/pingen/{token} Pingen-Briefstatus und Kosten übernehmen Empfängt einen Pingen-Rückruf. Das Pfadsegment token ist hier tatsächlich der Status-/Ereignistyp (z. B. send, error, not_deliverable), kein Authentifizierungstoken. data.relationships.letter.data.id identifiziert den externen Brief. Lädt dessen Status bei Pingen, aktualisiert lokalen Briefstatus und kann bei Rechnungen/Mahnungen Portokosten buchen. Der GET-Alias führt denselben schreibenden Handler aus; nicht als Leseabfrage verwenden. - Operation-ID: post_v2_auth_webhook_pingen_token - Dokumentation: /docs/api/v2/operations/post-v2-auth-webhook-pingen-token-ffc061155585/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Für diesen Endpunkt deklariert die Spezifikation keine OpenAPI-Authentifizierung (`security: []`). Weitere Bedingungen stehen in der Beschreibung und den edoobox-Hinweisen. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[],"securitySchemes":{}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | token | path | ja | Legacy-Name token im Pfad. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"token","in":"path","required":true,"description":"Legacy-Name token im Pfad.","schema":{"type":"string","description":"Status-/Ereignistyp des Pingen-Callbacks; kein Schlüssel.","examples":["send"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${PATH_TOKEN:?PATH_TOKEN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "relationships": { "letter": { "data": { "id": "synthetic-external-letter-id" } } } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth/webhook/pingen/'"${PATH_TOKEN}"'' \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth/webhook/pingen/{token}"; path = path.replaceAll("{token}", encodeURIComponent(required("PATH_TOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "relationships": { "letter": { "data": { "id": "synthetic-external-letter-id" } } } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth/webhook/pingen/{token}" path = path.replace("{token}", urllib.parse.quote(os.environ["PATH_TOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Content-Type"] = "application/json" body = "{\"data\":{\"relationships\":{\"letter\":{\"data\":{\"id\":\"synthetic-external-letter-id\"}}}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth_webhook_pingen_token","summary":"Pingen-Briefstatus und Kosten übernehmen","description":"Empfängt einen Pingen-Rückruf. Das Pfadsegment token ist hier tatsächlich der Status-/Ereignistyp (z. B. send, error, not_deliverable), kein Authentifizierungstoken. data.relationships.letter.data.id identifiziert den externen Brief. Lädt dessen Status bei Pingen, aktualisiert lokalen Briefstatus und kann bei Rechnungen/Mahnungen Portokosten buchen. Der GET-Alias führt denselben schreibenden Handler aus; nicht als Leseabfrage verwenden.","tags":["auth"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"description":"Legacy-Name token im Pfad.","schema":{"type":"string","description":"Status-/Ereignistyp des Pingen-Callbacks; kein Schlüssel.","examples":["send"]}}],"responses":{"200":{"description":"Text OK nach Verarbeitung des Rückrufs.","content":{"text/plain":{"schema":{"type":"string"},"example":"OK"}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"relationships":{"type":"object","properties":{"letter":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"additionalProperties":true,"required":["id"]}},"additionalProperties":true,"required":["data"]}},"additionalProperties":true,"required":["letter"]}},"additionalProperties":true,"required":["relationships"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Pingen-Briefstatus und Kosten übernehmen","value":{"data":{"relationships":{"letter":{"data":{"id":"synthetic-external-letter-id"}}}}}}}}}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/controllers/V2.php","method":"coreV2","line":28},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"description":"No account-JWT resource permission levels are applied. Authentication requirements, if any, are defined by this route source-specific boundary and dedicated security scheme.","checkedByDispatcher":false,"levels":[]}} ``` --- # GET /v2/auth2fa Zweite Faktoren auflisten Liest nicht gelöschte zweite Faktoren im gewählten Konto mit öffentlicher ID, zugehörigem Administrator, Typ und Erstellzeit. Feldauswahl und Filter folgen dem normalen GET-Vertrag; vertrauliche Faktor-/Telefoninformationen entsprechend geschützt behandeln. - Operation-ID: get_v2_auth2fa - Dokumentation: /docs/api/v2/operations/get-v2-auth2fa-24717591861c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth2fa' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth2fa","summary":"Zweite Faktoren auflisten","description":"Liest nicht gelöschte zweite Faktoren im gewählten Konto mit öffentlicher ID, zugehörigem Administrator, Typ und Erstellzeit. Feldauswahl und Filter folgen dem normalen GET-Vertrag; vertrauliche Faktor-/Telefoninformationen entsprechend geschützt behandeln.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"auth2fa_012345abcdef_123456":{"id":"auth2fa_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Auth2fa.php","line":300},"reason":"The five known GET metadata routes only select rows. backupkey switches the in-memory Structure before GET_data; passkey delegates to the SELECT-only _list. No enrollment, key generation, rotation or deletion runs on these GET branches.","relatedSources":[{"file":"application/models/v2/Passkeyfunc.php","line":115},{"file":"application/models/v2/Auth2fa.php","line":122}]},"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-authentication-metadata-read":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"getAuth2fa","line":300},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/auth2fa Zweiten Faktor in zwei Schritten einrichten Richtet einen Faktor für den angemeldeten Administrator ein. Zuerst type=phone mit phone+delivery, type=yubikey mit yubikey oder type=otpauth ohne eigenes Secret senden. Danach den zurückgegebenen review mit einem neuen YubiKey-OTP bzw. dem Telefon-/Authenticator-code zurückgeben. Erst die Bestätigung speichert den Faktor. admin muss der eigenen öffentlichen Administrator-ID entsprechen. Der Wire-Typ otpauth wird vom Structure-Eingabegetter für den Setup-Hook intern zu google normalisiert. Einrichtung kann SMS/Anruf auslösen und geheimes QR-/Review-Material zurückgeben. - Operation-ID: post_v2_auth2fa - Dokumentation: /docs/api/v2/operations/post-v2-auth2fa-f6c45c4faf6f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "admin": "admin_012345abcdef_123456", "type": "otpauth" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth2fa' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "admin": "admin_012345abcdef_123456", "type": "otpauth" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"admin\":\"admin_012345abcdef_123456\",\"type\":\"otpauth\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth2fa","summary":"Zweiten Faktor in zwei Schritten einrichten","description":"Richtet einen Faktor für den angemeldeten Administrator ein. Zuerst type=phone mit phone+delivery, type=yubikey mit yubikey oder type=otpauth ohne eigenes Secret senden. Danach den zurückgegebenen review mit einem neuen YubiKey-OTP bzw. dem Telefon-/Authenticator-code zurückgeben. Erst die Bestätigung speichert den Faktor. admin muss der eigenen öffentlichen Administrator-ID entsprechen. Der Wire-Typ otpauth wird vom Structure-Eingabegetter für den Setup-Hook intern zu google normalisiert. Einrichtung kann SMS/Anruf auslösen und geheimes QR-/Review-Material zurückgeben.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Vorbereitung: insert=false, review und valid, bei Authenticator zusätzlich geheime qrcode-URI. Bestätigung: nativer Insert-Bericht mit öffentlicher Faktor-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":false,"review":"synthetic-enrollment-review","valid":"otpauth","qrcode":"otpauth://totp/alex%40example.invalid?secret=SYNTHETIC&issuer=edoobox"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"review":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"valid":{"type":"string"},"qrcode":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"id":{"type":"string","pattern":"^auth2fa_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["admin","type"],"properties":{"admin":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Existing tenant-bound public admin v2 ID; never a database key."},"type":{"type":"string","enum":["phone","yubikey","otpauth"]},"otpauth":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true},"phone":{"type":"string"},"yubikey":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true},"review":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true},"code":{"type":["string","integer"],"writeOnly":true,"x-edoobox-sensitive":true},"delivery":{"type":"string"}},"additionalProperties":true,"description":"Two-stage enrollment: initial phone also needs phone+delivery; yubikey needs yubikey. Follow-up review requires the returned protected review material; phone/OTP follow-up needs code. Conditional security validation remains authoritative in Auth2fafunc::_before_add2FA. Single enrollments are the supported client workflow.","anyOf":[{"type":"object","properties":{"type":{"const":"phone"}},"additionalProperties":true,"required":["type","phone","delivery"]},{"type":"object","properties":{"type":{"const":"phone"}},"additionalProperties":true,"required":["type","phone","review","code"]},{"type":"object","properties":{"type":{"const":"yubikey"}},"additionalProperties":true,"required":["type","yubikey"]},{"type":"object","properties":{"type":{"const":"otpauth"}},"additionalProperties":true,"required":["type"]}],"allOf":[{"if":{"required":["review"],"properties":{"type":{"enum":["phone","otpauth"]}}},"then":{"required":["code"]}}]}},"additionalProperties":true},"examples":{"request":{"summary":"Zweiten Faktor in zwei Schritten einrichten","value":{"data":{"admin":"admin_012345abcdef_123456","type":"otpauth"}}}}}}},"x-edoobox-user-handoff":{"sensitivePaths":["/data/qrcode","/data/review"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/qrcode","/data/review"],"x-edoobox-sensitive-request-paths":["/data/review","/data/code","/data/yubikey","/data/otpauth"],"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-account-security-mutation":true,"x-edoobox-account-security-source":{"file":"application/models/v2/Auth2fa.php","line":184,"reason":"Authenticated account credential enrollment, recovery and deletion are account mutations. Preserve JWT tenant/current-role permissions and mutation approval. Enrollment/recovery material requires a protected human handoff; browser authenticator cryptography remains a human step."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"postAuth2fa","line":188},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/auth2fa/{id} Zweiten Faktor deaktivieren Markiert den gewählten zweiten Faktor im Konto als gelöscht. data.delete meldet, ob eine Zeile geändert wurde; es werden keine Ersatzcodes erzeugt. - Operation-ID: delete_v2_auth2fa_id - Dokumentation: /docs/api/v2/operations/delete-v2-auth2fa-id-dab15c8961f4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_auth2fa_id","summary":"Zweiten Faktor deaktivieren","description":"Markiert den gewählten zweiten Faktor im Konto als gelöscht. data.delete meldet, ob eine Zeile geändert wurde; es werden keine Ersatzcodes erzeugt.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Ziel-ID und Boolean delete.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"auth2fa_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^auth2fa_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^auth2fa_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"examples":{"request":{"summary":"Zweiten Faktor deaktivieren","value":{}}}}}},"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-account-security-mutation":true,"x-edoobox-account-security-source":{"file":"application/models/v2/Auth2fa.php","line":184,"reason":"Authenticated account credential enrollment, recovery and deletion are account mutations. Preserve JWT tenant/current-role permissions and mutation approval. Enrollment/recovery material requires a protected human handoff; browser authenticator cryptography remains a human step."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"deleteAuth2fa","line":253},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/auth2fa/{id} Zweite Faktoren lesen Liest nicht gelöschte zweite Faktoren im gewählten Konto mit öffentlicher ID, zugehörigem Administrator, Typ und Erstellzeit. Feldauswahl und Filter folgen dem normalen GET-Vertrag; vertrauliche Faktor-/Telefoninformationen entsprechend geschützt behandeln. - Operation-ID: get_v2_auth2fa_id - Dokumentation: /docs/api/v2/operations/get-v2-auth2fa-id-d2d98456bade/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth2fa_id","summary":"Zweite Faktoren lesen","description":"Liest nicht gelöschte zweite Faktoren im gewählten Konto mit öffentlicher ID, zugehörigem Administrator, Typ und Erstellzeit. Feldauswahl und Filter folgen dem normalen GET-Vertrag; vertrauliche Faktor-/Telefoninformationen entsprechend geschützt behandeln.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"auth2fa_012345abcdef_123456":{"id":"auth2fa_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Auth2fa.php","line":300},"reason":"The five known GET metadata routes only select rows. backupkey switches the in-memory Structure before GET_data; passkey delegates to the SELECT-only _list. No enrollment, key generation, rotation or deletion runs on these GET branches.","relatedSources":[{"file":"application/models/v2/Passkeyfunc.php","line":115},{"file":"application/models/v2/Auth2fa.php","line":122}]},"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-authentication-metadata-read":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"getAuth2fa","line":300},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/auth2fa/{id}/passkey Eigenen Passkey entfernen Markiert den aktiven Passkey ausschließlich des angemeldeten Administrators als gelöscht. data.delete meldet, ob eine Zeile geändert wurde; es werden keine Ersatzcodes erzeugt. - Operation-ID: delete_v2_auth2fa_id_passkey - Dokumentation: /docs/api/v2/operations/delete-v2-auth2fa-id-passkey-befe083719db/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/'"${PATH_ID}"'/passkey' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/{id}/passkey"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/{id}/passkey" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_auth2fa_id_passkey","summary":"Eigenen Passkey entfernen","description":"Markiert den aktiven Passkey ausschließlich des angemeldeten Administrators als gelöscht. data.delete meldet, ob eine Zeile geändert wurde; es werden keine Ersatzcodes erzeugt.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Ziel-ID und Boolean delete.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"passkey_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^passkey_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^passkey_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"examples":{"request":{"summary":"Eigenen Passkey entfernen","value":{}}}}}},"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-account-security-mutation":true,"x-edoobox-account-security-source":{"file":"application/models/v2/Auth2fa.php","line":184,"reason":"Authenticated account credential enrollment, recovery and deletion are account mutations. Preserve JWT tenant/current-role permissions and mutation approval. Enrollment/recovery material requires a protected human handoff; browser authenticator cryptography remains a human step."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"deleteAuth2fa","line":253,"branch":{"file":"application/models/v2/Auth2fa.php","line":258}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/auth2fa/list Zweite Faktoren auflisten Liest nicht gelöschte zweite Faktoren im gewählten Konto mit öffentlicher ID, zugehörigem Administrator, Typ und Erstellzeit. Feldauswahl und Filter folgen dem normalen GET-Vertrag; vertrauliche Faktor-/Telefoninformationen entsprechend geschützt behandeln. - Operation-ID: get_v2_auth2fa_list - Dokumentation: /docs/api/v2/operations/get-v2-auth2fa-list-10fb6813e09c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth2fa_list","summary":"Zweite Faktoren auflisten","description":"Liest nicht gelöschte zweite Faktoren im gewählten Konto mit öffentlicher ID, zugehörigem Administrator, Typ und Erstellzeit. Feldauswahl und Filter folgen dem normalen GET-Vertrag; vertrauliche Faktor-/Telefoninformationen entsprechend geschützt behandeln.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"auth2fa_012345abcdef_123456":{"id":"auth2fa_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["auth2fa_012345abcdef_123456"]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["phone","yubikey","otpauth"]},"phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"yubikey":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":88},"otpauth":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":98},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Auth2fa.php","line":108,"lineStart":108,"lineEnd":118,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"ca548c98cc9ea85d4f685868602285d0278bec6188bf1cad2b44909ff1e01112","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Passkey/backupkey actions bypass ordinary Structure insert."},{"file":"application/models/v2/Auth2fafunc.php","line":5,"lineStart":5,"lineEnd":78,"sha256":"fc66e5db9d9cf938b4e639b6d321f03ac9d7a85a51beb4d064b9716fc46b8458","spanSha256":"6968d29a7f5d76139e897bb3e0ad4762cb98549231d283ae5c70fe63ae9d7320","reason":"Setup/review may return existing DataUP or emit setup response without ordinary insert; date supplied is not used."},{"file":"application/models/v2/Auth2fa.php","line":188,"lineStart":188,"lineEnd":219,"sha256":"d1530f1f08622a277d20657dcec448d9710b49ac75be2e92d7df14c977ee244a","spanSha256":"b8e8897e70ad8c3a186d35af94e8ce3476226d4050c4c924fda583ecf37a927f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Auth2fa.php","line":300},"reason":"The five known GET metadata routes only select rows. backupkey switches the in-memory Structure before GET_data; passkey delegates to the SELECT-only _list. No enrollment, key generation, rotation or deletion runs on these GET branches.","relatedSources":[{"file":"application/models/v2/Passkeyfunc.php","line":115},{"file":"application/models/v2/Auth2fa.php","line":122}]},"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-authentication-metadata-read":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"getAuth2fa","line":300},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/auth2fa/list/backupkey Metadaten der Wiederherstellungscodes lesen Liest nicht gelöschte Backupcode-Metadaten mit id, admin, first5 und date_create über den normalen GET-Vertrag. Vollständige Backupcodes werden ausschließlich beim Erzeugen zurückgegeben; hier nur Präfixe. Die Konto- und Feldauswahl folgen dem nativen GET-Vertrag. - Operation-ID: get_v2_auth2fa_list_backupkey - Dokumentation: /docs/api/v2/operations/get-v2-auth2fa-list-backupkey-554b29ef5b38/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/list/backupkey' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/list/backupkey"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/list/backupkey" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth2fa_list_backupkey","summary":"Metadaten der Wiederherstellungscodes lesen","description":"Liest nicht gelöschte Backupcode-Metadaten mit id, admin, first5 und date_create über den normalen GET-Vertrag. Vollständige Backupcodes werden ausschließlich beim Erzeugen zurückgegeben; hier nur Präfixe. Die Konto- und Feldauswahl folgen dem nativen GET-Vertrag.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"auth2fa_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Backupcode-Metadaten mit Präfix first5, keine vollständigen Wiederherstellungscodes.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"auth2fabu_012345abcdef_123456":{"id":"auth2fabu_012345abcdef_123456","admin":"admin_012345abcdef_123456","first5":"ABCDE"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Normale ID-indizierte GET-Liste oder Array mit response_type=array.","anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^auth2fabu_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"first5":{"type":"string","x-edoobox-sensitive":true},"date_create":{"type":["string","boolean"],"description":"Datum im nativen dQ-Ausgabeformat."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^auth2fabu_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"first5":{"type":"string","x-edoobox-sensitive":true},"date_create":{"type":["string","boolean"],"description":"Datum im nativen dQ-Ausgabeformat."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Auth2fa.php","line":300},"reason":"The five known GET metadata routes only select rows. backupkey switches the in-memory Structure before GET_data; passkey delegates to the SELECT-only _list. No enrollment, key generation, rotation or deletion runs on these GET branches.","relatedSources":[{"file":"application/models/v2/Passkeyfunc.php","line":115},{"file":"application/models/v2/Auth2fa.php","line":122}]},"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-authentication-metadata-read":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"getAuth2fa","line":300,"branch":{"file":"application/models/v2/Auth2fa.php","line":311}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/auth2fa/list/backupkey Zehn neue Wiederherstellungscodes erzeugen Deaktiviert alle bisherigen Backupcodes des angemeldeten Administrators im Konto und erzeugt zehn neue Codes. Die vollständigen neuen Codes werden einmalig in data.backupkey ausgegeben. Ein übergebenes admin darf nur die eigene öffentliche Administrator-ID sein; admin ist in diesem Sonderzweig optional. - Operation-ID: post_v2_auth2fa_list_backupkey - Dokumentation: /docs/api/v2/operations/post-v2-auth2fa-list-backupkey-db711fcef063/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/list/backupkey' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/list/backupkey"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/list/backupkey" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth2fa_list_backupkey","summary":"Zehn neue Wiederherstellungscodes erzeugen","description":"Deaktiviert alle bisherigen Backupcodes des angemeldeten Administrators im Konto und erzeugt zehn neue Codes. Die vollständigen neuen Codes werden einmalig in data.backupkey ausgegeben. Ein übergebenes admin darf nur die eigene öffentliche Administrator-ID sein; admin ist in diesem Sonderzweig optional.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"insert=true und genau zehn vollständige neue Backupcodes; vertraulich behandeln.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"backupkey":["SYNTHETIC-BACKUP-01","SYNTHETIC-BACKUP-02","SYNTHETIC-BACKUP-03","SYNTHETIC-BACKUP-04","SYNTHETIC-BACKUP-05","SYNTHETIC-BACKUP-06","SYNTHETIC-BACKUP-07","SYNTHETIC-BACKUP-08","SYNTHETIC-BACKUP-09","SYNTHETIC-BACKUP-10"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"backupkey":{"type":"array","minItems":10,"maxItems":10,"items":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."}}},"additionalProperties":true,"required":["insert","backupkey"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zehn neue Wiederherstellungscodes erzeugen","value":{"data":{}}}}}}},"x-edoobox-user-handoff":{"sensitivePaths":["/data/backupkey"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/backupkey"],"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-account-security-mutation":true,"x-edoobox-account-security-source":{"file":"application/models/v2/Auth2fa.php","line":184,"reason":"Authenticated account credential enrollment, recovery and deletion are account mutations. Preserve JWT tenant/current-role permissions and mutation approval. Enrollment/recovery material requires a protected human handoff; browser authenticator cryptography remains a human step."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"postAuth2fa","line":188,"branch":{"file":"application/models/v2/Auth2fa.php","line":212}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/auth2fa/list/passkey Eigene Passkeys auflisten Liest ausschließlich aktive Passkeys des angemeldeten Administrators im gewählten Konto. Allgemeine Filter/Feldauswahl der CRUD-Liste werden in diesem Zweig nicht verwendet. Private Schlüssel oder vollständige Credentials werden nicht ausgegeben. - Operation-ID: get_v2_auth2fa_list_passkey - Dokumentation: /docs/api/v2/operations/get-v2-auth2fa-list-passkey-1fd7654b8b6e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/list/passkey' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/list/passkey"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/list/passkey" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_auth2fa_list_passkey","summary":"Eigene Passkeys auflisten","description":"Liest ausschließlich aktive Passkeys des angemeldeten Administrators im gewählten Konto. Allgemeine Filter/Feldauswahl der CRUD-Liste werden in diesem Zweig nicht verwendet. Private Schlüssel oder vollständige Credentials werden nicht ausgegeben.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Liste aus öffentlicher Passkey-ID, Label sowie Unixzeitpunkten für Erstellung und letzte Verwendung.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"passkey_012345abcdef_123456","label":"Arbeitsgerät","date_create":1790000000,"date_last_used":0}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^passkey_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"label":{"type":"string"},"date_create":{"type":"integer"},"date_last_used":{"type":"integer"}},"additionalProperties":true,"required":["id","label","date_create","date_last_used"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Auth2fa.php","line":300},"reason":"The five known GET metadata routes only select rows. backupkey switches the in-memory Structure before GET_data; passkey delegates to the SELECT-only _list. No enrollment, key generation, rotation or deletion runs on these GET branches.","relatedSources":[{"file":"application/models/v2/Passkeyfunc.php","line":115},{"file":"application/models/v2/Auth2fa.php","line":122}]},"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-authentication-metadata-read":true,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"getAuth2fa","line":300,"branch":{"file":"application/models/v2/Auth2fa.php","line":305}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/auth2fa/list/passkey Browser-Passkey registrieren Verifiziert das vom Browser erzeugte Credential gegen die zuvor ausgestellte, einmalige ceremony und den aktuellen Konto-/Administrator-/Login-Kontext. Speichert nur den validierten öffentlichen Schlüssel; doppelte Credentials werden abgelehnt. label wird getrimmt und auf 100 Zeichen gekürzt. admin ist optional und darf nur die eigene öffentliche ID sein. - Operation-ID: post_v2_auth2fa_list_passkey - Dokumentation: /docs/api/v2/operations/post-v2-auth2fa-list-passkey-6faf1a71d7d3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "ceremony": "synthetic-registration-ceremony", "label": "Arbeitsgerät", "credential": { "id": "c3ludGhldGljLWNyZWRlbnRpYWw", "rawId": "c3ludGhldGljLWNyZWRlbnRpYWw", "type": "public-key", "response": { "clientDataJSON": "c3ludGhldGlj", "attestationObject": "c3ludGhldGlj" } } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/list/passkey' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/list/passkey"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "ceremony": "synthetic-registration-ceremony", "label": "Arbeitsgerät", "credential": { "id": "c3ludGhldGljLWNyZWRlbnRpYWw", "rawId": "c3ludGhldGljLWNyZWRlbnRpYWw", "type": "public-key", "response": { "clientDataJSON": "c3ludGhldGlj", "attestationObject": "c3ludGhldGlj" } } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/list/passkey" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"ceremony\":\"synthetic-registration-ceremony\",\"label\":\"Arbeitsgerät\",\"credential\":{\"id\":\"c3ludGhldGljLWNyZWRlbnRpYWw\",\"rawId\":\"c3ludGhldGljLWNyZWRlbnRpYWw\",\"type\":\"public-key\",\"response\":{\"clientDataJSON\":\"c3ludGhldGlj\",\"attestationObject\":\"c3ludGhldGlj\"}}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth2fa_list_passkey","summary":"Browser-Passkey registrieren","description":"Verifiziert das vom Browser erzeugte Credential gegen die zuvor ausgestellte, einmalige ceremony und den aktuellen Konto-/Administrator-/Login-Kontext. Speichert nur den validierten öffentlichen Schlüssel; doppelte Credentials werden abgelehnt. label wird getrimmt und auf 100 Zeichen gekürzt. admin ist optional und darf nur die eigene öffentliche ID sein.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Passkey-ID, Label und Unixzeitpunkte; kein privater Schlüssel.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"passkey_012345abcdef_123456","label":"Arbeitsgerät","date_create":1790000000,"date_last_used":0}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^passkey_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"label":{"type":"string"},"date_create":{"type":"integer"},"date_last_used":{"type":"integer"}},"additionalProperties":true,"required":["id","label","date_create","date_last_used"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."},"ceremony":{"type":"string","writeOnly":true,"x-edoobox-sensitive":true,"description":"Nur synthetisches Beispiel; tatsächlich zurückgegebenes geschütztes Material sicher übergeben."},"credential":{"type":"object","additionalProperties":true,"description":"WebAuthn PublicKeyCredential als JSON aus navigator.credentials.create(); Bibliothek prüft Attestation, Origin und Challenge.","x-edoobox-sensitive":true,"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]+$","x-edoobox-id-kind":"webauthn-credential","description":"Base64URL-codierte Authenticator-Credential-Bytes; keine edoobox-Objekt- oder Datenbank-ID.","examples":["c3ludGhldGljLWNyZWRlbnRpYWw"]},"rawId":{"type":"string"},"type":{"type":"string"},"response":{"type":"object","additionalProperties":true},"clientExtensionResults":{"type":"object","additionalProperties":true}},"writeOnly":true},"label":{"description":"Kanonisch Text; serverseitig auf maximal 100 Zeichen gekürzt, Standard Passkey.","examples":["Arbeitsgerät"]}},"additionalProperties":true,"required":["ceremony","credential"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Browser-Passkey registrieren","value":{"data":{"ceremony":"synthetic-registration-ceremony","label":"Arbeitsgerät","credential":{"id":"c3ludGhldGljLWNyZWRlbnRpYWw","rawId":"c3ludGhldGljLWNyZWRlbnRpYWw","type":"public-key","response":{"clientDataJSON":"c3ludGhldGlj","attestationObject":"c3ludGhldGlj"}}}}}}}}},"x-edoobox-sensitive-request-paths":["/data/ceremony","/data/credential"],"x-edoobox-exampleStatus":"Requires a real browser-generated authenticator credential, user consent, and the matching ceremony. The synthetic example demonstrates wire shape only.","x-edoobox-runtime-preconditions":["The deployment must install web-auth/webauthn-lib ^5.3 and its dependencies. Composer declaration alone does not establish an available runtime. A trusted configured client Origin and a real browser authenticator ceremony are additionally required."],"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-account-security-mutation":true,"x-edoobox-account-security-source":{"file":"application/models/v2/Auth2fa.php","line":184,"reason":"Authenticated account credential enrollment, recovery and deletion are account mutations. Preserve JWT tenant/current-role permissions and mutation approval. Enrollment/recovery material requires a protected human handoff; browser authenticator cryptography remains a human step."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"postAuth2fa","line":188,"branch":{"file":"application/models/v2/Auth2fa.php","line":206}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/auth2fa/list/passkey-options Browser-Challenge zur Passkey-Registrierung starten Erstellt für den angemeldeten Administrator eine fünf Minuten gültige, einmal verwendbare Registrierungszeremonie mit WebAuthn-Optionen. Der Origin muss zur konfigurierten erlaubten Produktionsherkunft passen. Danach Browser-Credential mit ceremony an /auth2fa/list/passkey senden; admin ist optional und darf nur die eigene öffentliche ID sein. - Operation-ID: post_v2_auth2fa_list_passkey_options - Dokumentation: /docs/api/v2/operations/post-v2-auth2fa-list-passkey-options-7b35b9910aea/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | Origin | header | ja | The configured allowed client origin is required by Passkey::resolveOrigin. MCP supplies only its trusted operator-configured client origin; model arguments cannot choose an origin. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"Origin","in":"header","required":true,"description":"The configured allowed client origin is required by Passkey::resolveOrigin. MCP supplies only its trusted operator-configured client origin; model arguments cannot choose an origin.","schema":{"type":"string","format":"uri"},"example":"https://app2.edoobox.com"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${HEADER_ORIGIN:?HEADER_ORIGIN setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/auth2fa/list/passkey-options' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Origin: '"${HEADER_ORIGIN}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/auth2fa/list/passkey-options"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers["Origin"] = "" + required("HEADER_ORIGIN"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/auth2fa/list/passkey-options" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Origin"] = "" + os.environ["HEADER_ORIGIN"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_auth2fa_list_passkey_options","summary":"Browser-Challenge zur Passkey-Registrierung starten","description":"Erstellt für den angemeldeten Administrator eine fünf Minuten gültige, einmal verwendbare Registrierungszeremonie mit WebAuthn-Optionen. Der Origin muss zur konfigurierten erlaubten Produktionsherkunft passen. Danach Browser-Credential mit ceremony an /auth2fa/list/passkey senden; admin ist optional und darf nur die eigene öffentliche ID sein.","tags":["auth2fa"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"Origin","in":"header","required":true,"description":"The configured allowed client origin is required by Passkey::resolveOrigin. MCP supplies only its trusted operator-configured client origin; model arguments cannot choose an origin.","schema":{"type":"string","format":"uri"},"example":"https://app2.edoobox.com"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Geschützter ceremony-Token und WebAuthn PublicKeyCredentialCreationOptions; echte Optionen unverändert an den Browser übergeben.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"ceremony":"synthetic-registration-ceremony","options":{"challenge":"c3ludGhldGljLWNoYWxsZW5nZQ","rp":{"name":"edoobox","id":"app2.edoobox.com"},"user":{"id":"c3ludGhldGljLXVzZXI","name":"alex@example.invalid","displayName":"Alex Beispiel"},"pubKeyCredParams":[{"type":"public-key","alg":-7}],"timeout":300000,"attestation":"none","authenticatorSelection":{"residentKey":"required","userVerification":"required"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"ceremony":{"type":"string","x-edoobox-sensitive":true,"description":"Geschütztes Authentifizierungs- oder Wiederherstellungsmaterial; nicht protokollieren."},"options":{"type":"object","additionalProperties":true}},"additionalProperties":true,"required":["ceremony","options"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Browser-Challenge zur Passkey-Registrierung starten","value":{"data":{}}}}}}},"x-edoobox-user-handoff":{"sensitivePaths":["/data/ceremony","/data/options"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/ceremony","/data/options"],"x-edoobox-client-origin":true,"x-edoobox-runtime-preconditions":["The deployment must install web-auth/webauthn-lib ^5.3 and its dependencies. Composer declaration alone does not establish an available runtime. A trusted configured client Origin and a real browser authenticator ceremony are additionally required."],"x-edoobox-sensitiveResponseFields":["otpauth","yubikey","qrcode","review","backupkey","first5"],"x-edoobox-account-security-mutation":true,"x-edoobox-account-security-source":{"file":"application/models/v2/Auth2fa.php","line":184,"reason":"Authenticated account credential enrollment, recovery and deletion are account mutations. Preserve JWT tenant/current-role permissions and mutation approval. Enrollment/recovery material requires a protected human handoff; browser authenticator cryptography remains a human step."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Auth2fa.php","method":"postAuth2fa","line":188,"branch":{"file":"application/models/v2/Auth2fa.php","line":201}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"auth2fa","short":"2fa","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/automationrule Automationsregel entfernen Markiert die Regel als gelöscht und setzt ihren Status auf paused. Die Regel wird aus den normalen Listen und weiteren Regelaktionen ausgeschlossen; Ausführungshistorie wird nicht physisch gelöscht. data wird nicht verwendet. - Operation-ID: delete_v2_automationrule - Dokumentation: /docs/api/v2/operations/delete-v2-automationrule-9f24f78ed5e7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Regel-ID; erforderlich als Query oder JSON-Envelope id. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto.","examples":["automationrule_012345abcdef_123456"]},"description":"Regel-ID; erforderlich als Query oder JSON-Envelope id."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "automationrule_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/automationrule' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "automationrule_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"automationrule_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_automationrule","summary":"Automationsregel entfernen","description":"Markiert die Regel als gelöscht und setzt ihren Status auf paused. Die Regel wird aus den normalen Listen und weiteren Regelaktionen ausgeschlossen; Ausführungshistorie wird nicht physisch gelöscht. data wird nicht verwendet.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto.","examples":["automationrule_012345abcdef_123456"]},"description":"Regel-ID; erforderlich als Query oder JSON-Envelope id."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"delete":true,"id":"automationrule_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"delete":{"const":true},"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"additionalProperties":true,"required":["delete","id"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"additionalProperties":true},"examples":{"request":{"summary":"Automationsregel entfernen","value":{"id":"automationrule_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["id"]},{"body":["id"]}],"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"deleteAutomationrule","line":226},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/automationrule Automationsregeln suchen Liest nicht gelöschte Regeln des Kontos mit Definition, Validierungsfehlern und effektivem Status. status=invalid sucht gespeicherte Definitionsfehler, sonst draft/active/paused nur gültige Regeln. Optional trigger und search in Name/Beschreibung; unbekannte status/trigger werden ignoriert. Reihenfolge updated_at und ID absteigend. meta und limit enthalten total, reply/limit und start/offset. Pagination nur per limit.start/reply; ein separates offset greift nach Dispatcher-Normalisierung nicht. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: get_v2_automationrule - Dokumentation: /docs/api/v2/operations/get-v2-automationrule-04b9bbece2c0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply; Standard Offset 0, reply 100. Tatsächliche Seitengröße wird auf 1–200 begrenzt. | | trigger | query | nein | Auslöser; bestimmt verfügbare Kontextfelder und Aktionen. | | search | query | nein | Teiltext in Name oder Beschreibung. | | status | query | nein | Effektiver Statusfilter. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply; Standard Offset 0, reply 100. Tatsächliche Seitengröße wird auf 1–200 begrenzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":200,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":200}]},"example":{"start":0,"reply":25}}}},{"name":"trigger","in":"query","required":false,"description":"Auslöser; bestimmt verfügbare Kontextfelder und Aktionen.","schema":{"type":"string","enum":["api.post.user","api.post.booking","api.post.offer","communication.booking_confirmation.before_send","communication.certificate.before_send"]}},{"name":"search","in":"query","required":false,"description":"Teiltext in Name oder Beschreibung.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Effektiver Statusfilter.","schema":{"type":"string","enum":["draft","active","paused","invalid"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/automationrule' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_automationrule","summary":"Automationsregeln suchen","description":"Liest nicht gelöschte Regeln des Kontos mit Definition, Validierungsfehlern und effektivem Status. status=invalid sucht gespeicherte Definitionsfehler, sonst draft/active/paused nur gültige Regeln. Optional trigger und search in Name/Beschreibung; unbekannte status/trigger werden ignoriert. Reihenfolge updated_at und ID absteigend. meta und limit enthalten total, reply/limit und start/offset. Pagination nur per limit.start/reply; ein separates offset greift nach Dispatcher-Normalisierung nicht. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply; Standard Offset 0, reply 100. Tatsächliche Seitengröße wird auf 1–200 begrenzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":200,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":200}]},"example":{"start":0,"reply":25}}}},{"name":"trigger","in":"query","required":false,"description":"Auslöser; bestimmt verfügbare Kontextfelder und Aktionen.","schema":{"type":"string","enum":["api.post.user","api.post.booking","api.post.offer","communication.booking_confirmation.before_send","communication.certificate.before_send"]}},{"name":"search","in":"query","required":false,"description":"Teiltext in Name oder Beschreibung.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Effektiver Statusfilter.","schema":{"type":"string","enum":["draft","active","paused","invalid"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"automationrule_012345abcdef_123456","name":"Neue Kunden begrüßen","description":"Notiz nach API-Erstellung","status":"draft","stored_status":"draft","trigger":"api.post.user","priority":100,"condition_group":{"combinator":"and","conditions":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"]}],"summary":{"conditions":0,"actions":1},"definition_version":1,"valid":true,"validation_errors":[],"activated_at":null,"created_at":"2026-10-22T10:00:00+02:00","updated_at":"2026-10-22T10:00:00+02:00"}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["draft","active","paused","invalid"]},"stored_status":{"type":"string","enum":["draft","active","paused"]},"trigger":{"type":"string"},"priority":{"type":"integer"},"condition_group":{"anyOf":[{"type":["object","array","string","number","boolean"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},{"type":["object","array","string","number","boolean"]}],"description":"Normalisierte Definition. Gespeicherte draft/paused-Regeln können trotz erfolgreicher Antwort ungültige Inhalte enthalten; valid und validation_errors beachten."},"actions":{"type":"array","items":{"description":"Normalisierte Aktion oder fehlerhafter Entwurfseintrag; Aktivierung erfordert vollständige Validierung.","anyOf":[{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":"array","items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte."},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}}},{"type":["object","array","string","number","boolean","null"]}]}},"summary":{"type":"object","properties":{"conditions":{"type":"integer"},"actions":{"type":"integer"}},"additionalProperties":true,"required":["conditions","actions"]},"definition_version":{"type":"integer"},"valid":{"type":"boolean"},"validation_errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}},"activated_at":{"type":["string","null"]},"created_at":{"type":["string","null"]},"updated_at":{"type":["string","null"]}},"additionalProperties":true,"required":["id","name","description","status","stored_status","trigger","priority","condition_group","actions","summary","definition_version","valid","validation_errors","activated_at","created_at","updated_at"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Automationrule.php","line":38},"reason":"Read rules, existing execution history or static catalog/samples; no execution is started."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"getAutomationrule","line":38},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/automationrule Automationsregel erstellen Speichert eine neue Regel mit Name und Trigger. Standard draft und Priorität 100. Ein aktiver Status verlangt eine vollständig gültige Definition und konto-eigene Referenzen; fehlerhafte Draft-/Paused-Definitionen können mit validation_errors gespeichert werden. Unsichere rohe IDs werden in jedem Status abgewiesen. definition oder condition_group/actions sind möglich; öffentliche Regel-ID aus der Antwort für Folgeaktionen verwenden. Es wird noch keine Regelaktion ausgeführt. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: post_v2_automationrule - Dokumentation: /docs/api/v2/operations/post-v2-automationrule-c38368da0dd4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Neue Kunden begrüßen", "trigger": "api.post.user", "status": "draft", "condition_group": { "combinator": "and", "conditions": [] }, "actions": [ { "type": "note.create", "subject": "Neuer Kunde aus API", "message": "Datensatz prüfen", "targets": [ "user" ] } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/automationrule' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Neue Kunden begrüßen", "trigger": "api.post.user", "status": "draft", "condition_group": { "combinator": "and", "conditions": [] }, "actions": [ { "type": "note.create", "subject": "Neuer Kunde aus API", "message": "Datensatz prüfen", "targets": [ "user" ] } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Neue Kunden begrüßen\",\"trigger\":\"api.post.user\",\"status\":\"draft\",\"condition_group\":{\"combinator\":\"and\",\"conditions\":[]},\"actions\":[{\"type\":\"note.create\",\"subject\":\"Neuer Kunde aus API\",\"message\":\"Datensatz prüfen\",\"targets\":[\"user\"]}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_automationrule","summary":"Automationsregel erstellen","description":"Speichert eine neue Regel mit Name und Trigger. Standard draft und Priorität 100. Ein aktiver Status verlangt eine vollständig gültige Definition und konto-eigene Referenzen; fehlerhafte Draft-/Paused-Definitionen können mit validation_errors gespeichert werden. Unsichere rohe IDs werden in jedem Status abgewiesen. definition oder condition_group/actions sind möglich; öffentliche Regel-ID aus der Antwort für Folgeaktionen verwenden. Es wird noch keine Regelaktion ausgeführt. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"automationrule_012345abcdef_123456","name":"Neue Kunden begrüßen","description":"Notiz nach API-Erstellung","status":"draft","stored_status":"draft","trigger":"api.post.user","priority":100,"condition_group":{"combinator":"and","conditions":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"]}],"summary":{"conditions":0,"actions":1},"definition_version":1,"valid":true,"validation_errors":[],"activated_at":null,"created_at":"2026-10-22T10:00:00+02:00","updated_at":"2026-10-22T10:00:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"const":true},"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["draft","active","paused","invalid"]},"stored_status":{"type":"string","enum":["draft","active","paused"]},"trigger":{"type":"string"},"priority":{"type":"integer"},"condition_group":{"anyOf":[{"type":["object","array","string","number","boolean"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},{"type":["object","array","string","number","boolean"]}],"description":"Normalisierte Definition. Gespeicherte draft/paused-Regeln können trotz erfolgreicher Antwort ungültige Inhalte enthalten; valid und validation_errors beachten."},"actions":{"type":"array","items":{"description":"Normalisierte Aktion oder fehlerhafter Entwurfseintrag; Aktivierung erfordert vollständige Validierung.","anyOf":[{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":"array","items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte."},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}}},{"type":["object","array","string","number","boolean","null"]}]}},"summary":{"type":"object","properties":{"conditions":{"type":"integer"},"actions":{"type":"integer"}},"additionalProperties":true,"required":["conditions","actions"]},"definition_version":{"type":"integer"},"valid":{"type":"boolean"},"validation_errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}},"activated_at":{"type":["string","null"]},"created_at":{"type":["string","null"]},"updated_at":{"type":["string","null"]}},"additionalProperties":true,"required":["insert","id","name","description","status","stored_status","trigger","priority","condition_group","actions","summary","definition_version","valid","validation_errors","activated_at","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":["string","number","boolean"],"description":"Nach String-Konvertierung und Trim nicht leer, höchstens 160 Zeichen."},"description":{"type":["string","number","boolean","null"],"description":"Nach Trim leer → null; sonst auf 4000 Zeichen gekürzt."},"trigger":{"type":["string","number","boolean"],"description":"Auslöser aus dem Katalog; eine aktive Regel benötigt einen unterstützten Trigger."},"status":{"type":"string","description":"draft (Standard), active oder paused; Kleinschreibung/Whitespace werden normalisiert. Fehlerhafte Definitionen dürfen als draft/paused gespeichert werden, unsichere ID-Formen werden immer abgewiesen."},"priority":{"type":["integer","number","string","boolean"],"description":"Standard 100; nach Integer-Konvertierung auf -10000…10000 begrenzt. Niedrigere Werte werden zuerst ausgeführt."},"definition":{"type":["object","array"],"properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"condition_group":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"condition":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"description":"Definition in Version 1; condition ist Eingabealias für condition_group. Fehlende Version wird auf 1 gesetzt. Alternativ condition_group/actions direkt in data senden; explizites definition hat Vorrang."},"condition_group":{"type":["object","array","string","number","boolean","null"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":["array","object","string","number","boolean","null"],"items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"required":["name","trigger"]}},"additionalProperties":true,"required":["data"]},{"type":"object","properties":{"name":{"type":["string","number","boolean"],"description":"Nach String-Konvertierung und Trim nicht leer, höchstens 160 Zeichen."},"description":{"type":["string","number","boolean","null"],"description":"Nach Trim leer → null; sonst auf 4000 Zeichen gekürzt."},"trigger":{"type":["string","number","boolean"],"description":"Auslöser aus dem Katalog; eine aktive Regel benötigt einen unterstützten Trigger."},"status":{"type":"string","description":"draft (Standard), active oder paused; Kleinschreibung/Whitespace werden normalisiert. Fehlerhafte Definitionen dürfen als draft/paused gespeichert werden, unsichere ID-Formen werden immer abgewiesen."},"priority":{"type":["integer","number","string","boolean"],"description":"Standard 100; nach Integer-Konvertierung auf -10000…10000 begrenzt. Niedrigere Werte werden zuerst ausgeführt."},"definition":{"type":["object","array"],"properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"condition_group":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"condition":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"description":"Definition in Version 1; condition ist Eingabealias für condition_group. Fehlende Version wird auf 1 gesetzt. Alternativ condition_group/actions direkt in data senden; explizites definition hat Vorrang."},"condition_group":{"type":["object","array","string","number","boolean","null"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":["array","object","string","number","boolean","null"],"items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"required":["name","trigger"]}]},"examples":{"request":{"summary":"Automationsregel erstellen","value":{"data":{"name":"Neue Kunden begrüßen","trigger":"api.post.user","status":"draft","condition_group":{"combinator":"and","conditions":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"]}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"postAutomationrule","line":74},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/automationrule Automationsregel bearbeiten Aktualisiert die angegebenen Regelfelder; fehlende Name/Trigger/Status/Priorität und Definitionsteile bleiben erhalten. data darf nicht leer sein. Aktive Regeln müssen nach der Änderung gültig sein; Draft/Paused darf Definitionsfehler speichern. Neue Aktivierung setzt activated_at. Keine Aktion wird dadurch sofort ausgeführt und es gibt keine Versionssperre auf Regel-Ebene. Auf der Collection-Route ist id als oberstes Feld des JSON-Envelopes erforderlich; PUT übernimmt keine Querydaten. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: put_v2_automationrule - Dokumentation: /docs/api/v2/operations/put-v2-automationrule-fb57ec8ada79/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Begrüßung prüfen", "priority": 20 }, "id": "automationrule_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/automationrule' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Begrüßung prüfen", "priority": 20 }, "id": "automationrule_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Begrüßung prüfen\",\"priority\":20},\"id\":\"automationrule_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_automationrule","summary":"Automationsregel bearbeiten","description":"Aktualisiert die angegebenen Regelfelder; fehlende Name/Trigger/Status/Priorität und Definitionsteile bleiben erhalten. data darf nicht leer sein. Aktive Regeln müssen nach der Änderung gültig sein; Draft/Paused darf Definitionsfehler speichern. Neue Aktivierung setzt activated_at. Keine Aktion wird dadurch sofort ausgeführt und es gibt keine Versionssperre auf Regel-Ebene. Auf der Collection-Route ist id als oberstes Feld des JSON-Envelopes erforderlich; PUT übernimmt keine Querydaten. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"automationrule_012345abcdef_123456","name":"Begrüßung prüfen","description":"Notiz nach API-Erstellung","status":"draft","stored_status":"draft","trigger":"api.post.user","priority":20,"condition_group":{"combinator":"and","conditions":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"]}],"summary":{"conditions":0,"actions":1},"definition_version":1,"valid":true,"validation_errors":[],"activated_at":null,"created_at":"2026-10-22T10:00:00+02:00","updated_at":"2026-10-22T10:00:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"const":true},"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["draft","active","paused","invalid"]},"stored_status":{"type":"string","enum":["draft","active","paused"]},"trigger":{"type":"string"},"priority":{"type":"integer"},"condition_group":{"anyOf":[{"type":["object","array","string","number","boolean"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},{"type":["object","array","string","number","boolean"]}],"description":"Normalisierte Definition. Gespeicherte draft/paused-Regeln können trotz erfolgreicher Antwort ungültige Inhalte enthalten; valid und validation_errors beachten."},"actions":{"type":"array","items":{"description":"Normalisierte Aktion oder fehlerhafter Entwurfseintrag; Aktivierung erfordert vollständige Validierung.","anyOf":[{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":"array","items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte."},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}}},{"type":["object","array","string","number","boolean","null"]}]}},"summary":{"type":"object","properties":{"conditions":{"type":"integer"},"actions":{"type":"integer"}},"additionalProperties":true,"required":["conditions","actions"]},"definition_version":{"type":"integer"},"valid":{"type":"boolean"},"validation_errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}},"activated_at":{"type":["string","null"]},"created_at":{"type":["string","null"]},"updated_at":{"type":["string","null"]}},"additionalProperties":true,"required":["update","id","name","description","status","stored_status","trigger","priority","condition_group","actions","summary","definition_version","valid","validation_errors","activated_at","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":["string","number","boolean"],"description":"Nach String-Konvertierung und Trim nicht leer, höchstens 160 Zeichen."},"description":{"type":["string","number","boolean","null"],"description":"Nach Trim leer → null; sonst auf 4000 Zeichen gekürzt."},"trigger":{"type":["string","number","boolean"],"description":"Auslöser aus dem Katalog; eine aktive Regel benötigt einen unterstützten Trigger."},"status":{"type":"string","description":"draft (Standard), active oder paused; Kleinschreibung/Whitespace werden normalisiert. Fehlerhafte Definitionen dürfen als draft/paused gespeichert werden, unsichere ID-Formen werden immer abgewiesen."},"priority":{"type":["integer","number","string","boolean"],"description":"Standard 100; nach Integer-Konvertierung auf -10000…10000 begrenzt. Niedrigere Werte werden zuerst ausgeführt."},"definition":{"type":["object","array"],"properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"condition_group":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"condition":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"description":"Definition in Version 1; condition ist Eingabealias für condition_group. Fehlende Version wird auf 1 gesetzt. Alternativ condition_group/actions direkt in data senden; explizites definition hat Vorrang."},"condition_group":{"type":["object","array","string","number","boolean","null"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":["array","object","string","number","boolean","null"],"items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"minProperties":1},"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Automationsregel bearbeiten","value":{"data":{"name":"Begrüßung prüfen","priority":20},"id":"automationrule_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"updateAutomationrule","line":157},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/automationrule/{id} Automationsregel entfernen Markiert die Regel als gelöscht und setzt ihren Status auf paused. Die Regel wird aus den normalen Listen und weiteren Regelaktionen ausgeschlossen; Ausführungshistorie wird nicht physisch gelöscht. data wird nicht verwendet. - Operation-ID: delete_v2_automationrule_id - Dokumentation: /docs/api/v2/operations/delete-v2-automationrule-id-679cfbb05ecc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/automationrule/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_automationrule_id","summary":"Automationsregel entfernen","description":"Markiert die Regel als gelöscht und setzt ihren Status auf paused. Die Regel wird aus den normalen Listen und weiteren Regelaktionen ausgeschlossen; Ausführungshistorie wird nicht physisch gelöscht. data wird nicht verwendet.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"delete":true,"id":"automationrule_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"delete":{"const":true},"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"additionalProperties":true,"required":["delete","id"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Automationsregel entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"deleteAutomationrule","line":226},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/automationrule/{id} Automationsregel im Detail lesen Liest die Regel anhand ihrer öffentlichen Pfad-ID. Die Antwort enthält normalisierte Bedingungen/Aktionen, valid und validation_errors. status kann invalid sein, während stored_status den gespeicherten draft/active/paused-Status zeigt. Query-ID, filter, fields und Sortierung werden nicht zur Auswahl verwendet. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: get_v2_automationrule_id - Dokumentation: /docs/api/v2/operations/get-v2-automationrule-id-35ecd8934ba0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/automationrule/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_automationrule_id","summary":"Automationsregel im Detail lesen","description":"Liest die Regel anhand ihrer öffentlichen Pfad-ID. Die Antwort enthält normalisierte Bedingungen/Aktionen, valid und validation_errors. status kann invalid sein, während stored_status den gespeicherten draft/active/paused-Status zeigt. Query-ID, filter, fields und Sortierung werden nicht zur Auswahl verwendet. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"automationrule_012345abcdef_123456","name":"Neue Kunden begrüßen","description":"Notiz nach API-Erstellung","status":"draft","stored_status":"draft","trigger":"api.post.user","priority":100,"condition_group":{"combinator":"and","conditions":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"]}],"summary":{"conditions":0,"actions":1},"definition_version":1,"valid":true,"validation_errors":[],"activated_at":null,"created_at":"2026-10-22T10:00:00+02:00","updated_at":"2026-10-22T10:00:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["draft","active","paused","invalid"]},"stored_status":{"type":"string","enum":["draft","active","paused"]},"trigger":{"type":"string"},"priority":{"type":"integer"},"condition_group":{"anyOf":[{"type":["object","array","string","number","boolean"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},{"type":["object","array","string","number","boolean"]}],"description":"Normalisierte Definition. Gespeicherte draft/paused-Regeln können trotz erfolgreicher Antwort ungültige Inhalte enthalten; valid und validation_errors beachten."},"actions":{"type":"array","items":{"description":"Normalisierte Aktion oder fehlerhafter Entwurfseintrag; Aktivierung erfordert vollständige Validierung.","anyOf":[{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":"array","items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte."},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}}},{"type":["object","array","string","number","boolean","null"]}]}},"summary":{"type":"object","properties":{"conditions":{"type":"integer"},"actions":{"type":"integer"}},"additionalProperties":true,"required":["conditions","actions"]},"definition_version":{"type":"integer"},"valid":{"type":"boolean"},"validation_errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}},"activated_at":{"type":["string","null"]},"created_at":{"type":["string","null"]},"updated_at":{"type":["string","null"]}},"additionalProperties":true,"required":["id","name","description","status","stored_status","trigger","priority","condition_group","actions","summary","definition_version","valid","validation_errors","activated_at","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Automationrule.php","line":38},"reason":"Read rules, existing execution history or static catalog/samples; no execution is started."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"getAutomationrule","line":38},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/automationrule/{id} Automationsregel bearbeiten Aktualisiert die angegebenen Regelfelder; fehlende Name/Trigger/Status/Priorität und Definitionsteile bleiben erhalten. data darf nicht leer sein. Aktive Regeln müssen nach der Änderung gültig sein; Draft/Paused darf Definitionsfehler speichern. Neue Aktivierung setzt activated_at. Keine Aktion wird dadurch sofort ausgeführt und es gibt keine Versionssperre auf Regel-Ebene. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: put_v2_automationrule_id - Dokumentation: /docs/api/v2/operations/put-v2-automationrule-id-63ed4230bed5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Begrüßung prüfen", "priority": 20 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/automationrule/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Begrüßung prüfen", "priority": 20 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Begrüßung prüfen\",\"priority\":20}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_automationrule_id","summary":"Automationsregel bearbeiten","description":"Aktualisiert die angegebenen Regelfelder; fehlende Name/Trigger/Status/Priorität und Definitionsteile bleiben erhalten. data darf nicht leer sein. Aktive Regeln müssen nach der Änderung gültig sein; Draft/Paused darf Definitionsfehler speichern. Neue Aktivierung setzt activated_at. Keine Aktion wird dadurch sofort ausgeführt und es gibt keine Versionssperre auf Regel-Ebene. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"automationrule_012345abcdef_123456","name":"Begrüßung prüfen","description":"Notiz nach API-Erstellung","status":"draft","stored_status":"draft","trigger":"api.post.user","priority":20,"condition_group":{"combinator":"and","conditions":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"]}],"summary":{"conditions":0,"actions":1},"definition_version":1,"valid":true,"validation_errors":[],"activated_at":null,"created_at":"2026-10-22T10:00:00+02:00","updated_at":"2026-10-22T10:00:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"const":true},"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["draft","active","paused","invalid"]},"stored_status":{"type":"string","enum":["draft","active","paused"]},"trigger":{"type":"string"},"priority":{"type":"integer"},"condition_group":{"anyOf":[{"type":["object","array","string","number","boolean"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},{"type":["object","array","string","number","boolean"]}],"description":"Normalisierte Definition. Gespeicherte draft/paused-Regeln können trotz erfolgreicher Antwort ungültige Inhalte enthalten; valid und validation_errors beachten."},"actions":{"type":"array","items":{"description":"Normalisierte Aktion oder fehlerhafter Entwurfseintrag; Aktivierung erfordert vollständige Validierung.","anyOf":[{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":"array","items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte."},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}}},{"type":["object","array","string","number","boolean","null"]}]}},"summary":{"type":"object","properties":{"conditions":{"type":"integer"},"actions":{"type":"integer"}},"additionalProperties":true,"required":["conditions","actions"]},"definition_version":{"type":"integer"},"valid":{"type":"boolean"},"validation_errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}},"activated_at":{"type":["string","null"]},"created_at":{"type":["string","null"]},"updated_at":{"type":["string","null"]}},"additionalProperties":true,"required":["update","id","name","description","status","stored_status","trigger","priority","condition_group","actions","summary","definition_version","valid","validation_errors","activated_at","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":["string","number","boolean"],"description":"Nach String-Konvertierung und Trim nicht leer, höchstens 160 Zeichen."},"description":{"type":["string","number","boolean","null"],"description":"Nach Trim leer → null; sonst auf 4000 Zeichen gekürzt."},"trigger":{"type":["string","number","boolean"],"description":"Auslöser aus dem Katalog; eine aktive Regel benötigt einen unterstützten Trigger."},"status":{"type":"string","description":"draft (Standard), active oder paused; Kleinschreibung/Whitespace werden normalisiert. Fehlerhafte Definitionen dürfen als draft/paused gespeichert werden, unsichere ID-Formen werden immer abgewiesen."},"priority":{"type":["integer","number","string","boolean"],"description":"Standard 100; nach Integer-Konvertierung auf -10000…10000 begrenzt. Niedrigere Werte werden zuerst ausgeführt."},"definition":{"type":["object","array"],"properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"condition_group":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"condition":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"description":"Definition in Version 1; condition ist Eingabealias für condition_group. Fehlende Version wird auf 1 gesetzt. Alternativ condition_group/actions direkt in data senden; explizites definition hat Vorrang."},"condition_group":{"type":["object","array","string","number","boolean","null"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":["array","object","string","number","boolean","null"],"items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"minProperties":1}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Automationsregel bearbeiten","value":{"data":{"name":"Begrüßung prüfen","priority":20}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"updateAutomationrule","line":157},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/automationrule/{id}/activate Automationsregel aktivieren Validiert die gespeicherte Regel einschließlich aktueller Kontofelder, Tags und Vorlagen und setzt sie bei Erfolg auf active. Die Requestdaten werden ignoriert. Schlägt die Validierung fehl, werden Fehler dauerhaft gespeichert und eine zuvor aktive Regel wird auf paused gesetzt; auch die Fehlerantwort kann deshalb Zustand verändern. Aktivieren führt selbst keine Regelaktion aus. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: post_v2_automationrule_id_activate - Dokumentation: /docs/api/v2/operations/post-v2-automationrule-id-activate-6265e7c84c73/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/automationrule/'"${PATH_ID}"'/activate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/{id}/activate"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/{id}/activate" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_automationrule_id_activate","summary":"Automationsregel aktivieren","description":"Validiert die gespeicherte Regel einschließlich aktueller Kontofelder, Tags und Vorlagen und setzt sie bei Erfolg auf active. Die Requestdaten werden ignoriert. Schlägt die Validierung fehl, werden Fehler dauerhaft gespeichert und eine zuvor aktive Regel wird auf paused gesetzt; auch die Fehlerantwort kann deshalb Zustand verändern. Aktivieren führt selbst keine Regelaktion aus. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"active":true,"id":"automationrule_012345abcdef_123456","name":"Neue Kunden begrüßen","description":"Notiz nach API-Erstellung","status":"active","stored_status":"active","trigger":"api.post.user","priority":100,"condition_group":{"combinator":"and","conditions":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"]}],"summary":{"conditions":0,"actions":1},"definition_version":1,"valid":true,"validation_errors":[],"activated_at":"2026-10-22T10:00:00+02:00","created_at":"2026-10-22T10:00:00+02:00","updated_at":"2026-10-22T10:00:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"const":true},"active":{"const":true},"id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["draft","active","paused","invalid"]},"stored_status":{"type":"string","enum":["draft","active","paused"]},"trigger":{"type":"string"},"priority":{"type":"integer"},"condition_group":{"anyOf":[{"type":["object","array","string","number","boolean"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},{"type":["object","array","string","number","boolean"]}],"description":"Normalisierte Definition. Gespeicherte draft/paused-Regeln können trotz erfolgreicher Antwort ungültige Inhalte enthalten; valid und validation_errors beachten."},"actions":{"type":"array","items":{"description":"Normalisierte Aktion oder fehlerhafter Entwurfseintrag; Aktivierung erfordert vollständige Validierung.","anyOf":[{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":"array","items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte."},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}}},{"type":["object","array","string","number","boolean","null"]}]}},"summary":{"type":"object","properties":{"conditions":{"type":"integer"},"actions":{"type":"integer"}},"additionalProperties":true,"required":["conditions","actions"]},"definition_version":{"type":"integer"},"valid":{"type":"boolean"},"validation_errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}},"activated_at":{"type":["string","null"]},"created_at":{"type":["string","null"]},"updated_at":{"type":["string","null"]}},"additionalProperties":true,"required":["update","active","id","name","description","status","stored_status","trigger","priority","condition_group","actions","summary","definition_version","valid","validation_errors","activated_at","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Automationsregel aktivieren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"postAutomationrule","line":74,"branch":{"file":"application/models/v2/Automationrule.php","line":94}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/automationrule/{id}/simulate Automationsregel an einem Kontoobjekt simulieren Wertet die gespeicherte Regel an einem existierenden Kontoobjekt aus. resource_id ist bei gültiger Regel erforderlich und muss zum Trigger passen; Vorlagen/Tags/Felder werden zuvor validiert. Bei ungültiger Regel folgt matched=false mit validation.errors und trace=null, bevor resource_id geprüft wird. Liefert Treffer, wertfreie Auswertungsspur und nur bei Treffer die geplanten Aktionen; führt keine Aktionen aus und legt keinen Ausführungseintrag an. /test ist derselbe reine Simulationspfad. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: post_v2_automationrule_id_simulate - Dokumentation: /docs/api/v2/operations/post-v2-automationrule-id-simulate-3acb87f03ce1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "resource_id": "user_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/automationrule/'"${PATH_ID}"'/simulate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/{id}/simulate"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "resource_id": "user_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/{id}/simulate" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"resource_id\":\"user_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_automationrule_id_simulate","summary":"Automationsregel an einem Kontoobjekt simulieren","description":"Wertet die gespeicherte Regel an einem existierenden Kontoobjekt aus. resource_id ist bei gültiger Regel erforderlich und muss zum Trigger passen; Vorlagen/Tags/Felder werden zuvor validiert. Bei ungültiger Regel folgt matched=false mit validation.errors und trace=null, bevor resource_id geprüft wird. Liefert Treffer, wertfreie Auswertungsspur und nur bei Treffer die geplanten Aktionen; führt keine Aktionen aus und legt keinen Ausführungseintrag an. /test ist derselbe reine Simulationspfad. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"matched":true,"trigger":"api.post.user","validation":{"valid":true,"errors":[]},"trace":{"type":"group","combinator":"and","result":true,"children":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"],"valid":true}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"matched":{"type":"boolean"},"trigger":{"type":"string"},"validation":{"type":"object","properties":{"valid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}}},"additionalProperties":true,"required":["valid","errors"]},"trace":{"anyOf":[{"type":"object","properties":{"type":{"type":"string"},"field":{"type":"string"},"operator":{"type":"string"},"field_exists":{"type":"boolean"},"result":{"type":"boolean"}},"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string"},"combinator":{"type":"string"},"result":{"type":"boolean"},"children":{"type":"array","items":{"type":"object","additionalProperties":true,"description":"Weitere Gruppen- oder Bedingungsspur ohne tatsächliche Feldwerte."}}},"additionalProperties":true},{"type":"null"}]},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":"array","items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte."},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."},"valid":{"type":"boolean"}},"additionalProperties":true}}},"additionalProperties":true,"required":["matched","trigger","validation","trace","actions"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"data":{"type":"object","properties":{"resource_id":{"type":"string","pattern":"^(?:user|booking|offer|participant)_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche ID eines Kontoobjekts passend zum Trigger: api.post.user→user, api.post.booking/booking_confirmation→booking, api.post.offer→offer, certificate→participant. Kein künstliches Ereignis wird gespeichert."}},"additionalProperties":true,"required":["resource_id"]}},"additionalProperties":true,"required":["data"]},{"type":"object","properties":{"resource_id":{"type":"string","pattern":"^(?:user|booking|offer|participant)_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche ID eines Kontoobjekts passend zum Trigger: api.post.user→user, api.post.booking/booking_confirmation→booking, api.post.offer→offer, certificate→participant. Kein künstliches Ereignis wird gespeichert."}},"additionalProperties":true,"required":["resource_id"]}]},"examples":{"request":{"summary":"Automationsregel an einem Kontoobjekt simulieren","value":{"data":{"resource_id":"user_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"postAutomationrule","line":74,"branch":{"file":"application/models/v2/Automationrule.php","line":93}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/automationrule/{id}/test Automationsregel an einem Kontoobjekt simulieren Wertet die gespeicherte Regel an einem existierenden Kontoobjekt aus. resource_id ist bei gültiger Regel erforderlich und muss zum Trigger passen; Vorlagen/Tags/Felder werden zuvor validiert. Bei ungültiger Regel folgt matched=false mit validation.errors und trace=null, bevor resource_id geprüft wird. Liefert Treffer, wertfreie Auswertungsspur und nur bei Treffer die geplanten Aktionen; führt keine Aktionen aus und legt keinen Ausführungseintrag an. /test ist derselbe reine Simulationspfad. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: post_v2_automationrule_id_test - Dokumentation: /docs/api/v2/operations/post-v2-automationrule-id-test-08a9d15ef411/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "resource_id": "user_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/automationrule/'"${PATH_ID}"'/test' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/{id}/test"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "resource_id": "user_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/{id}/test" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"resource_id\":\"user_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_automationrule_id_test","summary":"Automationsregel an einem Kontoobjekt simulieren","description":"Wertet die gespeicherte Regel an einem existierenden Kontoobjekt aus. resource_id ist bei gültiger Regel erforderlich und muss zum Trigger passen; Vorlagen/Tags/Felder werden zuvor validiert. Bei ungültiger Regel folgt matched=false mit validation.errors und trace=null, bevor resource_id geprüft wird. Liefert Treffer, wertfreie Auswertungsspur und nur bei Treffer die geplanten Aktionen; führt keine Aktionen aus und legt keinen Ausführungseintrag an. /test ist derselbe reine Simulationspfad. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"matched":true,"trigger":"api.post.user","validation":{"valid":true,"errors":[]},"trace":{"type":"group","combinator":"and","result":true,"children":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"],"valid":true}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"matched":{"type":"boolean"},"trigger":{"type":"string"},"validation":{"type":"object","properties":{"valid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}}},"additionalProperties":true,"required":["valid","errors"]},"trace":{"anyOf":[{"type":"object","properties":{"type":{"type":"string"},"field":{"type":"string"},"operator":{"type":"string"},"field_exists":{"type":"boolean"},"result":{"type":"boolean"}},"additionalProperties":true},{"type":"object","properties":{"type":{"type":"string"},"combinator":{"type":"string"},"result":{"type":"boolean"},"children":{"type":"array","items":{"type":"object","additionalProperties":true,"description":"Weitere Gruppen- oder Bedingungsspur ohne tatsächliche Feldwerte."}}},"additionalProperties":true},{"type":"null"}]},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":"array","items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte."},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."},"valid":{"type":"boolean"}},"additionalProperties":true}}},"additionalProperties":true,"required":["matched","trigger","validation","trace","actions"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"data":{"type":"object","properties":{"resource_id":{"type":"string","pattern":"^(?:user|booking|offer|participant)_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche ID eines Kontoobjekts passend zum Trigger: api.post.user→user, api.post.booking/booking_confirmation→booking, api.post.offer→offer, certificate→participant. Kein künstliches Ereignis wird gespeichert."}},"additionalProperties":true,"required":["resource_id"]}},"additionalProperties":true,"required":["data"]},{"type":"object","properties":{"resource_id":{"type":"string","pattern":"^(?:user|booking|offer|participant)_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche ID eines Kontoobjekts passend zum Trigger: api.post.user→user, api.post.booking/booking_confirmation→booking, api.post.offer→offer, certificate→participant. Kein künstliches Ereignis wird gespeichert."}},"additionalProperties":true,"required":["resource_id"]}]},"examples":{"request":{"summary":"Automationsregel an einem Kontoobjekt simulieren","value":{"data":{"resource_id":"user_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"postAutomationrule","line":74,"branch":{"file":"application/models/v2/Automationrule.php","line":93}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/automationrule/{id}/validate Gespeicherte Automationsregel prüfen Prüft ausschließlich die bereits gespeicherte Regeldefinition und ihre aktuellen Konto-Referenzen. JSON-data wird ignoriert; die Prüfung aktualisiert weder Regelstatus noch gespeicherte Fehler. valid=false mit validation_errors ist ein erfolgreiches Prüfergebnis. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: post_v2_automationrule_id_validate - Dokumentation: /docs/api/v2/operations/post-v2-automationrule-id-validate-d3ca2ea763a0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/automationrule/'"${PATH_ID}"'/validate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/{id}/validate"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/{id}/validate" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_automationrule_id_validate","summary":"Gespeicherte Automationsregel prüfen","description":"Prüft ausschließlich die bereits gespeicherte Regeldefinition und ihre aktuellen Konto-Referenzen. JSON-data wird ignoriert; die Prüfung aktualisiert weder Regelstatus noch gespeicherte Fehler. valid=false mit validation_errors ist ein erfolgreiches Prüfergebnis. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["automationrule_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"valid":true,"validation_errors":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"valid":{"type":"boolean"},"validation_errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}}},"additionalProperties":true,"required":["valid","validation_errors"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Gespeicherte Automationsregel prüfen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"postAutomationrule","line":74,"branch":{"file":"application/models/v2/Automationrule.php","line":92}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/automationrule/catalog Automationsmöglichkeiten und Kontoreferenzen lesen Liest verfügbare Trigger, Kontextfelder, Datentypen, Operatoren und Aktionen sowie maximale Gruppentiefe/Anzahlen. Ohne trigger sind fields/actions nach Auslöser gruppiert, mit trigger flache Listen. account_options enthält die tatsächlich schreibbaren Benutzerfelder, geeignete E-Mail-Felder, Tags und zulässige E-Mail-/Teilnehmer-PDF-Vorlagen. Dynamische Kontofelder sind eine offene kontospezifische Konfiguration; gültige Referenzen aus diesem Katalog übernehmen. Kein Definitionsspeichern oder Ausführen. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: get_v2_automationrule_catalog - Dokumentation: /docs/api/v2/operations/get-v2-automationrule-catalog-ce6249144c0a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | trigger | query | nein | Auslöser; bestimmt verfügbare Kontextfelder und Aktionen. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"trigger","in":"query","required":false,"description":"Auslöser; bestimmt verfügbare Kontextfelder und Aktionen.","schema":{"type":"string","enum":["api.post.user","api.post.booking","api.post.offer","communication.booking_confirmation.before_send","communication.certificate.before_send"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/automationrule/catalog' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/catalog"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/catalog" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_automationrule_catalog","summary":"Automationsmöglichkeiten und Kontoreferenzen lesen","description":"Liest verfügbare Trigger, Kontextfelder, Datentypen, Operatoren und Aktionen sowie maximale Gruppentiefe/Anzahlen. Ohne trigger sind fields/actions nach Auslöser gruppiert, mit trigger flache Listen. account_options enthält die tatsächlich schreibbaren Benutzerfelder, geeignete E-Mail-Felder, Tags und zulässige E-Mail-/Teilnehmer-PDF-Vorlagen. Dynamische Kontofelder sind eine offene kontospezifische Konfiguration; gültige Referenzen aus diesem Katalog übernehmen. Kein Definitionsspeichern oder Ausführen. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"trigger","in":"query","required":false,"description":"Auslöser; bestimmt verfügbare Kontextfelder und Aktionen.","schema":{"type":"string","enum":["api.post.user","api.post.booking","api.post.offer","communication.booking_confirmation.before_send","communication.certificate.before_send"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"definition_version":1,"triggers":[{"key":"api.post.user","contexts":["user"],"fields":["user.email"],"actions":["note.create"]}],"fields":[{"key":"user.email","entity":"user","property":"email","data_type":"text"}],"operators":{"text":["equals","not_equals","contains","is_empty","is_not_empty"]},"actions":[{"type":"note.create","contexts_any":["user","booking"],"execution":"async","parameters":{"subject":"text","message":"text","targets":"note_target_list"},"target_types":["user"]}],"limits":{"group_depth":2,"conditions":50,"actions":10},"account_options":{"user_data_fields":[],"user_writable_fields":[],"user_email_fields":[],"tags":[],"email_templates":[],"pdf_templates":[]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"definition_version":{"type":"integer"},"triggers":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"contexts":{"type":"array","items":{"type":"string"}},"fields":{"type":"array","items":{"type":"string"}},"actions":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","contexts","fields","actions"]}},"fields":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"entity":{"type":"string"},"property":{"type":"string"},"data_type":{"type":"string"},"dynamic":{"type":"boolean"},"source_key":{"type":"string"},"label":{"type":"string"},"input_type":{"type":"string"},"enabled":{"type":"boolean"},"read_only":{"type":"boolean"}},"additionalProperties":true,"required":["key","entity","property","data_type"]}},{"type":"array","items":{"type":"object","properties":{"trigger":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"entity":{"type":"string"},"property":{"type":"string"},"data_type":{"type":"string"},"dynamic":{"type":"boolean"},"source_key":{"type":"string"},"label":{"type":"string"},"input_type":{"type":"string"},"enabled":{"type":"boolean"},"read_only":{"type":"boolean"}},"additionalProperties":true,"required":["key","entity","property","data_type"]}}},"additionalProperties":true,"required":["trigger","fields"]}}]},"operators":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"actions":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"contexts":{"type":"array","items":{"type":"string"}},"contexts_any":{"type":"array","items":{"type":"string"}},"execution":{"type":"string","enum":["async","synchronous"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}},"field_source":{"type":"string"},"writable_fields":{"type":"array","items":{"type":"string"}},"recipient_types":{"type":"array","items":{"type":"string"}},"recipient_field_source":{"type":"string"},"target_types":{"type":"array","items":{"type":"string"}},"template_kind":{"type":"string"},"template_compatibility":{"type":"string"}},"additionalProperties":true,"required":["type","execution","parameters"]}},{"type":"array","items":{"type":"object","properties":{"trigger":{"type":"string"},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"contexts":{"type":"array","items":{"type":"string"}},"contexts_any":{"type":"array","items":{"type":"string"}},"execution":{"type":"string","enum":["async","synchronous"]},"parameters":{"type":"object","additionalProperties":{"type":"string"}},"field_source":{"type":"string"},"writable_fields":{"type":"array","items":{"type":"string"}},"recipient_types":{"type":"array","items":{"type":"string"}},"recipient_field_source":{"type":"string"},"target_types":{"type":"array","items":{"type":"string"}},"template_kind":{"type":"string"},"template_compatibility":{"type":"string"}},"additionalProperties":true,"required":["type","execution","parameters"]}}},"additionalProperties":true,"required":["trigger","actions"]}}],"items":{"description":"Normalisierte Aktion oder fehlerhafter Entwurfseintrag; Aktivierung erfordert vollständige Validierung.","anyOf":[{},{"type":["object","array","string","number","boolean","null"]}]}},"limits":{"type":"object","properties":{"group_depth":{"type":"integer"},"conditions":{"type":"integer"},"actions":{"type":"integer"}},"additionalProperties":true,"required":["group_depth","conditions","actions"]},"account_options":{"type":"object","properties":{"user_data_fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"entity":{"type":"string"},"property":{"type":"string"},"data_type":{"type":"string"},"dynamic":{"type":"boolean"},"source_key":{"type":"string"},"label":{"type":"string"},"input_type":{"type":"string"},"enabled":{"type":"boolean"},"read_only":{"type":"boolean"}},"additionalProperties":true,"required":["key","entity","property","data_type"]}},"user_writable_fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"entity":{"type":"string"},"property":{"type":"string"},"data_type":{"type":"string"},"dynamic":{"type":"boolean"},"source_key":{"type":"string"},"label":{"type":"string"},"input_type":{"type":"string"},"enabled":{"type":"boolean"},"read_only":{"type":"boolean"}},"additionalProperties":true,"required":["key","entity","property","data_type"]}},"user_email_fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"entity":{"type":"string"},"property":{"type":"string"},"data_type":{"type":"string"},"dynamic":{"type":"boolean"},"source_key":{"type":"string"},"label":{"type":"string"},"input_type":{"type":"string"},"enabled":{"type":"boolean"},"read_only":{"type":"boolean"}},"additionalProperties":true,"required":["key","entity","property","data_type"]}},"tags":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"value":{"type":"string"},"contexts":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["id","name","value","contexts"]}},"email_templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"family_id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"display_name":{"type":"string"},"type":{"type":"string"},"kind":{"type":"string"},"compatibility":{"type":"string"},"languages":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["id","family_id","name","display_name","type","kind","compatibility","languages"]}},"pdf_templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"family_id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"display_name":{"type":"string"},"type":{"type":"string"},"kind":{"type":"string"},"compatibility":{"type":"string"},"languages":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["id","family_id","name","display_name","type","kind","compatibility","languages"]}}},"additionalProperties":true}},"additionalProperties":true,"required":["definition_version","triggers","fields","operators","actions","limits","account_options"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Automationrule.php","line":38},"reason":"Read rules, existing execution history or static catalog/samples; no execution is started."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"getAutomationrule","line":38,"branch":{"file":"application/models/v2/Automationrule.php","line":43}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/automationrule/executions Automationsausführungen und Aktionsstatus lesen Liest Ausführungshistorie des Kontos einschließlich Aktionsergebnissen. Filter nur trigger, rule und source_reference; status/search/filter werden ignoriert. Ohne source_reference entfällt der optionale subject-Snapshot meist aus Listen, mit gültiger Referenz wird Ereigniskontext geladen. Reihenfolge Ausführungs-ID absteigend. meta/limit enthalten Gesamtzahl und Seitengröße; meta.next_cursor liefert den before-Cursor. before überschreibt Offset und setzt ihn auf 0; total wird vor Cursorbegrenzung gezählt. Keine Wiederholung oder Ausführung von Aktionen. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: get_v2_automationrule_executions - Dokumentation: /docs/api/v2/operations/get-v2-automationrule-executions-25dcad288835/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply; Standard Offset 0, reply 100. Tatsächliche Seitengröße wird auf 1–200 begrenzt. | | trigger | query | nein | Auslöser; bestimmt verfügbare Kontextfelder und Aktionen. | | source_reference | query | nein | Öffentliche Ereignisreferenz (z.B. booking-/user-ID); nichtleere nicht rein numerische Referenz. Ungültige rein numerische Werte werden ignoriert. | | before | query | nein | Nächste Seite vor dieser Ausführungs-ID. meta.next_cursor aus der vorherigen Seite verwenden; setzt Offset auf 0. | | rule | query | nein | Ausführungen genau dieser Regel. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply; Standard Offset 0, reply 100. Tatsächliche Seitengröße wird auf 1–200 begrenzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":200,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":200}]},"example":{"start":0,"reply":25}}}},{"name":"trigger","in":"query","required":false,"description":"Auslöser; bestimmt verfügbare Kontextfelder und Aktionen.","schema":{"type":"string","enum":["api.post.user","api.post.booking","api.post.offer","communication.booking_confirmation.before_send","communication.certificate.before_send"]}},{"name":"source_reference","in":"query","required":false,"description":"Öffentliche Ereignisreferenz (z.B. booking-/user-ID); nichtleere nicht rein numerische Referenz. Ungültige rein numerische Werte werden ignoriert.","schema":{"type":"string"}},{"name":"before","in":"query","required":false,"description":"Nächste Seite vor dieser Ausführungs-ID. meta.next_cursor aus der vorherigen Seite verwenden; setzt Offset auf 0.","schema":{"type":"string","pattern":"^automationexecution_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},{"name":"rule","in":"query","required":false,"description":"Ausführungen genau dieser Regel.","schema":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/automationrule/executions' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/executions"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/executions" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_automationrule_executions","summary":"Automationsausführungen und Aktionsstatus lesen","description":"Liest Ausführungshistorie des Kontos einschließlich Aktionsergebnissen. Filter nur trigger, rule und source_reference; status/search/filter werden ignoriert. Ohne source_reference entfällt der optionale subject-Snapshot meist aus Listen, mit gültiger Referenz wird Ereigniskontext geladen. Reihenfolge Ausführungs-ID absteigend. meta/limit enthalten Gesamtzahl und Seitengröße; meta.next_cursor liefert den before-Cursor. before überschreibt Offset und setzt ihn auf 0; total wird vor Cursorbegrenzung gezählt. Keine Wiederholung oder Ausführung von Aktionen. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply; Standard Offset 0, reply 100. Tatsächliche Seitengröße wird auf 1–200 begrenzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":200,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":200}]},"example":{"start":0,"reply":25}}}},{"name":"trigger","in":"query","required":false,"description":"Auslöser; bestimmt verfügbare Kontextfelder und Aktionen.","schema":{"type":"string","enum":["api.post.user","api.post.booking","api.post.offer","communication.booking_confirmation.before_send","communication.certificate.before_send"]}},{"name":"source_reference","in":"query","required":false,"description":"Öffentliche Ereignisreferenz (z.B. booking-/user-ID); nichtleere nicht rein numerische Referenz. Ungültige rein numerische Werte werden ignoriert.","schema":{"type":"string"}},{"name":"before","in":"query","required":false,"description":"Nächste Seite vor dieser Ausführungs-ID. meta.next_cursor aus der vorherigen Seite verwenden; setzt Offset auf 0.","schema":{"type":"string","pattern":"^automationexecution_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},{"name":"rule","in":"query","required":false,"description":"Ausführungen genau dieser Regel.","schema":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"automationexecution_012345abcdef_123456","event_id":"automationevent_012345abcdef_123456","rule_id":"automationrule_012345abcdef_123456","rule_name":"Neue Kunden begrüßen","trigger":"api.post.user","status":"skipped","engine_status":"not_matched","matched":false,"priority":100,"source_type":"user","source_reference":"user_012345abcdef_123456","correlation_id":"synthetic-correlation-001","duration_ms":0,"created_at":"2026-10-22T10:00:00+02:00","completed_at":"2026-10-22T10:00:00+02:00","has_error":false,"actions":[]}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^automationexecution_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"event_id":{"type":"string","pattern":"^automationevent_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"rule_id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"rule_name":{"type":"string"},"trigger":{"type":"string"},"status":{"type":"string","enum":["success","running","skipped","failed"]},"engine_status":{"type":"string"},"matched":{"type":"boolean"},"priority":{"type":"integer"},"source_type":{"type":"string"},"source_reference":{"type":["string","null"]},"correlation_id":{"type":"string"},"duration_ms":{"type":"integer"},"created_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"has_error":{"type":"boolean"},"subject":{"type":"object","properties":{"participant_id":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"user_id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"}},"additionalProperties":true},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^automationactionexecution_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"type":{"type":"string"},"status":{"type":"string"},"action":{"type":["object","array","null"],"properties":{"type":{"type":"string"},"fields":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"items":{},"description":"Gespeicherte Aktion. Bei user.update nur Feldnamen, keine neuen Feldwerte."},"result":{"type":["object","array","null"],"additionalProperties":true,"items":{},"description":"Aktionsspezifisch; sensible Schlüssel redigiert und rohe *_id-Werte entfernt."},"template":{"type":["object","null"],"properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"type":{"type":"string"},"preview_available":{"type":"boolean"},"file_name":{"type":"string"}},"additionalProperties":true},"attempts":{"type":"integer"},"available_at":{"type":["string","null"],"format":"date-time"},"started_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"duration_ms":{"type":"integer"},"has_error":{"type":"boolean"},"error_message":{"type":["string","null"]}},"additionalProperties":true,"required":["id","type","status","action","result","template","attempts","available_at","started_at","completed_at","duration_ms","has_error","error_message"]}}},"additionalProperties":true,"required":["id","event_id","rule_id","rule_name","trigger","status","engine_status","matched","priority","source_type","source_reference","correlation_id","duration_ms","created_at","completed_at","has_error","actions"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Automationrule.php","line":38},"reason":"Read rules, existing execution history or static catalog/samples; no execution is started."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"getAutomationrule","line":38,"branch":{"file":"application/models/v2/Automationrule.php","line":49}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/automationrule/executions/{executionId} Einzelne Automationsausführung untersuchen Liest genau eine Ausführung anhand der automationexecution-ID im vierten Pfadabschnitt. Enthält Aktionen, gespeicherte Spur, sichere Eingabereferenz, optionalen Teilnehmer-/Benutzernamen und bereinigte Fehlermeldungen. Zeitstempel sind ISO-8601-Strings oder null; Status bildet Engine- und Aktionszustände ab. Keine Aktion wird gestartet oder wiederholt. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: get_v2_automationrule_executions_executionId - Dokumentation: /docs/api/v2/operations/get-v2-automationrule-executions-executionid-f3a7ca63234c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | executionId | path | ja | Öffentliche ID der Ausführung, nicht der Regel. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"executionId","in":"path","required":true,"description":"Öffentliche ID der Ausführung, nicht der Regel.","schema":{"type":"string","pattern":"^automationexecution_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_EXECUTIONID:?PATH_EXECUTIONID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/automationrule/executions/'"${PATH_EXECUTIONID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/executions/{executionId}"; path = path.replaceAll("{executionId}", encodeURIComponent(required("PATH_EXECUTIONID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/executions/{executionId}" path = path.replace("{executionId}", urllib.parse.quote(os.environ["PATH_EXECUTIONID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_automationrule_executions_executionId","summary":"Einzelne Automationsausführung untersuchen","description":"Liest genau eine Ausführung anhand der automationexecution-ID im vierten Pfadabschnitt. Enthält Aktionen, gespeicherte Spur, sichere Eingabereferenz, optionalen Teilnehmer-/Benutzernamen und bereinigte Fehlermeldungen. Zeitstempel sind ISO-8601-Strings oder null; Status bildet Engine- und Aktionszustände ab. Keine Aktion wird gestartet oder wiederholt. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"executionId","in":"path","required":true,"description":"Öffentliche ID der Ausführung, nicht der Regel.","schema":{"type":"string","pattern":"^automationexecution_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"automationexecution_012345abcdef_123456","event_id":"automationevent_012345abcdef_123456","rule_id":"automationrule_012345abcdef_123456","rule_name":"Neue Kunden begrüßen","trigger":"api.post.user","status":"skipped","engine_status":"not_matched","matched":false,"priority":100,"source_type":"user","source_reference":"user_012345abcdef_123456","correlation_id":"synthetic-correlation-001","duration_ms":0,"created_at":"2026-10-22T10:00:00+02:00","completed_at":"2026-10-22T10:00:00+02:00","has_error":false,"actions":[],"trace":null,"input":{"source_type":"user","source_reference":"user_012345abcdef_123456"},"error_message":null}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^automationexecution_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"event_id":{"type":"string","pattern":"^automationevent_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"rule_id":{"type":"string","pattern":"^automationrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"rule_name":{"type":"string"},"trigger":{"type":"string"},"status":{"type":"string","enum":["success","running","skipped","failed"]},"engine_status":{"type":"string"},"matched":{"type":"boolean"},"priority":{"type":"integer"},"source_type":{"type":"string"},"source_reference":{"type":["string","null"]},"correlation_id":{"type":"string"},"duration_ms":{"type":"integer"},"created_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"has_error":{"type":"boolean"},"subject":{"type":"object","properties":{"participant_id":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"user_id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"}},"additionalProperties":true},"actions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^automationactionexecution_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"type":{"type":"string"},"status":{"type":"string"},"action":{"type":["object","array","null"],"properties":{"type":{"type":"string"},"fields":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"items":{},"description":"Gespeicherte Aktion. Bei user.update nur Feldnamen, keine neuen Feldwerte."},"result":{"type":["object","array","null"],"additionalProperties":true,"items":{},"description":"Aktionsspezifisch; sensible Schlüssel redigiert und rohe *_id-Werte entfernt."},"template":{"type":["object","null"],"properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"name":{"type":"string"},"type":{"type":"string"},"preview_available":{"type":"boolean"},"file_name":{"type":"string"}},"additionalProperties":true},"attempts":{"type":"integer"},"available_at":{"type":["string","null"],"format":"date-time"},"started_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"duration_ms":{"type":"integer"},"has_error":{"type":"boolean"},"error_message":{"type":["string","null"]}},"additionalProperties":true,"required":["id","type","status","action","result","template","attempts","available_at","started_at","completed_at","duration_ms","has_error","error_message"]}},"trace":{"type":["object","array","null"],"additionalProperties":true,"items":{},"description":"Gespeicherte Auswertungsspur, ohne rekonstruiertes Live-Ereignis."},"input":{"type":"object","properties":{"source_type":{"type":"string"},"source_reference":{"type":["string","null"]}},"additionalProperties":true,"required":["source_type","source_reference"]},"error_message":{"type":["string","null"]}},"additionalProperties":true,"required":["id","event_id","rule_id","rule_name","trigger","status","engine_status","matched","priority","source_type","source_reference","correlation_id","duration_ms","created_at","completed_at","has_error","actions","trace","input","error_message"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Automationrule.php","line":38},"reason":"Read rules, existing execution history or static catalog/samples; no execution is started."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"getAutomationrule","line":37},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/automationrule/samples Kontoobjekte für eine Regelsimulation suchen Sucht existierende Kontoobjekte passend zum obligatorischen trigger. api.post.user nutzt Benutzer, api.post.booking und booking_confirmation Buchungen, api.post.offer Angebote, certificate Teilnehmer. Liefert öffentliche Auswahl-IDs, Anzeigenamen und Untertitel einschließlich möglicher personenbezogener Inhalte; keine Aktion oder Ausführung wird erzeugt. search wird auf 100 Zeichen begrenzt; keine Offset-Pagination oder Gesamtzahl. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: get_v2_automationrule_samples - Dokumentation: /docs/api/v2/operations/get-v2-automationrule-samples-6fa5b46cff2f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | limit | query | nein | Nur reply wird verwendet; start/Offset wird ignoriert. Dispatcherstandard 100 wird auf 50 reduziert; explizite positive Werte auf 1–50 begrenzt, Werte unter 1 führen zum Servicefallback 20. | | trigger | query | ja | Auslöser; bestimmt verfügbare Kontextfelder und Aktionen. | | search | query | nein | Suche in den zum Trigger passenden Kontoobjekten; maximal 100 Zeichen werden benutzt. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"limit","in":"query","required":false,"description":"Nur reply wird verwendet; start/Offset wird ignoriert. Dispatcherstandard 100 wird auf 50 reduziert; explizite positive Werte auf 1–50 begrenzt, Werte unter 1 führen zum Servicefallback 20.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":50,"default":50}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":50}]},"example":{"start":0,"reply":25}}}},{"name":"trigger","in":"query","required":true,"description":"Auslöser; bestimmt verfügbare Kontextfelder und Aktionen.","schema":{"type":"string","enum":["api.post.user","api.post.booking","api.post.offer","communication.booking_confirmation.before_send","communication.certificate.before_send"]}},{"name":"search","in":"query","required":false,"description":"Suche in den zum Trigger passenden Kontoobjekten; maximal 100 Zeichen werden benutzt.","schema":{"type":"string"}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_TRIGGER:?QUERY_TRIGGER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/automationrule/samples' \ --url-query 'trigger='"${QUERY_TRIGGER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/samples"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("trigger", required("QUERY_TRIGGER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/samples" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("trigger", os.environ["QUERY_TRIGGER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_automationrule_samples","summary":"Kontoobjekte für eine Regelsimulation suchen","description":"Sucht existierende Kontoobjekte passend zum obligatorischen trigger. api.post.user nutzt Benutzer, api.post.booking und booking_confirmation Buchungen, api.post.offer Angebote, certificate Teilnehmer. Liefert öffentliche Auswahl-IDs, Anzeigenamen und Untertitel einschließlich möglicher personenbezogener Inhalte; keine Aktion oder Ausführung wird erzeugt. search wird auf 100 Zeichen begrenzt; keine Offset-Pagination oder Gesamtzahl. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"Nur reply wird verwendet; start/Offset wird ignoriert. Dispatcherstandard 100 wird auf 50 reduziert; explizite positive Werte auf 1–50 begrenzt, Werte unter 1 führen zum Servicefallback 20.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":50,"default":50}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":50}]},"example":{"start":0,"reply":25}}}},{"name":"trigger","in":"query","required":true,"description":"Auslöser; bestimmt verfügbare Kontextfelder und Aktionen.","schema":{"type":"string","enum":["api.post.user","api.post.booking","api.post.offer","communication.booking_confirmation.before_send","communication.certificate.before_send"]}},{"name":"search","in":"query","required":false,"description":"Suche in den zum Trigger passenden Kontoobjekten; maximal 100 Zeichen werden benutzt.","schema":{"type":"string"}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"items":[{"id":"user_012345abcdef_123456","name":"Alex Beispiel","subtitle":"alex@example.com","resource_type":"user"}],"resource_type":"user"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^(?:user|booking|offer|participant)_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche ID eines Kontoobjekts passend zum Trigger: api.post.user→user, api.post.booking/booking_confirmation→booking, api.post.offer→offer, certificate→participant. Kein künstliches Ereignis wird gespeichert."},"name":{"type":"string"},"subtitle":{"type":"string"},"resource_type":{"type":"string","enum":["user","booking","offer","participant"]}},"additionalProperties":true,"required":["id","name","subtitle","resource_type"]}},"resource_type":{"type":"string","enum":["user","booking","offer","participant"]}},"additionalProperties":true,"required":["items","resource_type"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Automationrule.php","line":38},"reason":"Read rules, existing execution history or static catalog/samples; no execution is started."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"getAutomationrule","line":38,"branch":{"file":"application/models/v2/Automationrule.php","line":46}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/automationrule/validate Ungespeicherte Automationsdefinition prüfen Prüft eine vorgeschlagene Definition und ihre Referenzen ohne sie zu speichern. Fehlende/ungültige Angaben werden als valid=false und Pfad-/Fehlercode-Liste gemeldet; eine fachlich ungültige Definition ist selbst eine erfolgreiche Prüfanfrage. name/status sind hierfür irrelevant. Trigger entscheidet über Felder, Operatoren, Aktionen und Empfängerkontexte; aktuelle Kontofelder, Tags und Vorlagen werden kontrolliert. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben. - Operation-ID: post_v2_automationrule_validate - Dokumentation: /docs/api/v2/operations/post-v2-automationrule-validate-8d13f7467400/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "trigger": "api.post.user", "condition_group": { "combinator": "and", "conditions": [] }, "actions": [ { "type": "note.create", "subject": "Neuer Kunde aus API", "message": "Datensatz prüfen", "targets": [ "user" ] } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/automationrule/validate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/automationrule/validate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "trigger": "api.post.user", "condition_group": { "combinator": "and", "conditions": [] }, "actions": [ { "type": "note.create", "subject": "Neuer Kunde aus API", "message": "Datensatz prüfen", "targets": [ "user" ] } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/automationrule/validate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"trigger\":\"api.post.user\",\"condition_group\":{\"combinator\":\"and\",\"conditions\":[]},\"actions\":[{\"type\":\"note.create\",\"subject\":\"Neuer Kunde aus API\",\"message\":\"Datensatz prüfen\",\"targets\":[\"user\"]}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_automationrule_validate","summary":"Ungespeicherte Automationsdefinition prüfen","description":"Prüft eine vorgeschlagene Definition und ihre Referenzen ohne sie zu speichern. Fehlende/ungültige Angaben werden als valid=false und Pfad-/Fehlercode-Liste gemeldet; eine fachlich ungültige Definition ist selbst eine erfolgreiche Prüfanfrage. name/status sind hierfür irrelevant. Trigger entscheidet über Felder, Operatoren, Aktionen und Empfängerkontexte; aktuelle Kontofelder, Tags und Vorlagen werden kontrolliert. Regel-/Ausführungszeitstempel werden als ISO-8601-String oder null ausgegeben.","tags":["automationrule"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt bzw. die Ergebnisliste; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"valid":true,"validation_errors":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"valid":{"type":"boolean"},"validation_errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true,"required":["path","code"]}}},"additionalProperties":true,"required":["valid","validation_errors"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":false,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"data":{"type":"object","properties":{"trigger":{"type":["string","number","boolean"],"description":"Auslöser aus dem Katalog; eine aktive Regel benötigt einen unterstützten Trigger."},"definition":{"type":["object","array"],"properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"condition_group":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"condition":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"description":"Definition in Version 1; condition ist Eingabealias für condition_group. Fehlende Version wird auf 1 gesetzt. Alternativ condition_group/actions direkt in data senden; explizites definition hat Vorrang."},"condition_group":{"type":["object","array","string","number","boolean","null"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":["array","object","string","number","boolean","null"],"items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true}},"additionalProperties":true},{"type":"object","properties":{"trigger":{"type":["string","number","boolean"],"description":"Auslöser aus dem Katalog; eine aktive Regel benötigt einen unterstützten Trigger."},"definition":{"type":["object","array"],"properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"condition_group":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"condition":{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true,"description":"Definition in Version 1; condition ist Eingabealias für condition_group. Fehlende Version wird auf 1 gesetzt. Alternativ condition_group/actions direkt in data senden; explizites definition hat Vorrang."},"condition_group":{"type":["object","array","string","number","boolean","null"],"properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}},"items":{"type":["array","object"],"items":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]},"additionalProperties":{"anyOf":[{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},{"type":"object","properties":{"combinator":{"type":"string","description":"and/or (kleingeschrieben normalisiert), Standard and."},"operator":{"type":"string","description":"Eingabealias für combinator."},"conditions":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}},"items":{"type":["array","object"],"items":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]},"additionalProperties":{"type":"object","properties":{"field":{"type":"string","description":"Kontextfeld aus dem Katalog, z.B. user.email. Kontofelder user.data_fields.data_1 … data_30 nur gemäß account_options."},"data_type":{"type":"string","description":"Optional für feste Felder; bei dynamischen Kontofeldern erforderlich und mit dem Kontokatalog abzugleichen (z.B. text/date)."},"value_type":{"type":"string","description":"Eingabealias für data_type."},"operator":{"type":"string","description":"Operator aus dem Katalog. is_empty/is_not_empty/is_true/is_false benötigen keinen value."},"value":{"description":"Typ abhängig von Feld/Operator: Textstring (max. 2000 Zeichen), Zahl oder Zahlenpaar, ISO-Datum YYYY-MM-DD bzw. Datumspaar, Alter 0–150 oder Liste öffentlicher Tag-IDs. identifier-Vergleiche verlangen passende v2-ID. Kein Feldwert wird allein durch Simulation verändert."}},"additionalProperties":true,"required":["field","operator"]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."}]}}},"additionalProperties":true,"description":"Bedingungsgruppe; höchstens zwei Gruppenebenen und insgesamt 50 Blattbedingungen. Leere Gruppe trifft zu. conditions/items und combinator/operator sind Eingabealiase."},"actions":{"type":["array","object","string","number","boolean","null"],"items":{"type":"object","properties":{"type":{"type":"string","description":"user.update; user.tags.add/remove; email.send_template; note.create; communication.email_template.replace; communication.pdf_template.replace. Verfügbarkeit hängt vom Triggerkontext ab. Eingabealiase email.send sowie communication.*_template.override werden normalisiert."},"fields":{"type":"object","minProperties":1,"additionalProperties":{"type":["string","number","boolean","null"]},"description":"Offene, kontobezogene Feldwert-Map: user.first_name/last_name/gender/street/postcode/location/country/language/phone_number/email/company oder user.data_fields.data_N. Schreibbare Schlüssel und Typen aus GET /automationrule/catalog übernehmen; Werte müssen scalar oder null sein."},"tags":{"type":["array","object"],"minItems":1,"maxItems":50,"items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"additionalProperties":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."}},"template":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto."},"recipient":{"type":"object","properties":{"type":{"type":"string","enum":["user_field","offer_leaders"]},"field":{"type":"string","description":"Bei user_field erforderlich: user.email oder textbasiertes user.data_fields.data_N aus account_options.user_email_fields."}},"additionalProperties":true,"required":["type"]},"subject":{"type":"string","maxLength":255,"description":"Für note.create erforderlich und nach Trim nicht leer."},"message":{"type":"string","maxLength":1000,"default":""},"targets":{"type":["array","object"],"items":{"type":"string","enum":["user","booking"]},"minItems":1,"maxItems":2,"uniqueItems":true,"description":"Für note.create erforderlich; nur im Trigger vorhandene Kontexte.","additionalProperties":{"type":"string","enum":["user","booking"]}},"configuration":{"type":"object","additionalProperties":true,"description":"Eingabealias: Konfigurationsfelder werden neben type ausgepackt."}},"additionalProperties":true,"required":["type"]},"description":"Für eine gültige Regel 1–10 Aktionen. Nur auslöserspezifische Aktionen/Empfänger gemäß Katalog; alle Tags/Vorlagen und Kontofelder werden im aktuellen Konto geprüft."}},"additionalProperties":true}]},"examples":{"request":{"summary":"Ungespeicherte Automationsdefinition prüfen","value":{"data":{"trigger":"api.post.user","condition_group":{"combinator":"and","conditions":[]},"actions":[{"type":"note.create","subject":"Neuer Kunde aus API","message":"Datensatz prüfen","targets":["user"]}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Automationrule.php","method":"postAutomationrule","line":74,"branch":{"file":"application/models/v2/Automationrule.php","line":81}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"automationrule","short":"auru","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/avataradmin Administratoravatar entfernen Entfernt den ausgewählten Avatar. id darf entweder eine passende öffentliche files-ID oder eine admin-ID sein; bei einer Eigentümer-ID wird der neueste nicht gelöschte Avatar dieses Eigentümers gesucht. Es werden ausschließlich Avatare des passenden Ressourcentyps und Accounts gelöscht. - Operation-ID: delete_v2_avataradmin - Dokumentation: /docs/api/v2/operations/delete-v2-avataradmin-cedc6443937e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["avataradmin_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "admin_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/avataradmin' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/avataradmin"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "admin_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/avataradmin" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"admin_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_avataradmin","summary":"Administratoravatar entfernen","description":"Entfernt den ausgewählten Avatar. id darf entweder eine passende öffentliche files-ID oder eine admin-ID sein; bei einer Eigentümer-ID wird der neueste nicht gelöschte Avatar dieses Eigentümers gesucht. Es werden ausschließlich Avatare des passenden Ressourcentyps und Accounts gelöscht.","tags":["avataradmin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["avataradmin_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Normalisierte öffentliche files-ID und delete-Ergebnis; avatar_not_found bei fehlendem Avatar.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Administratoravatar entfernen","value":{"id":"admin_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Avataradmin.php","method":"deleteAvataradmin","line":49},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"avataradmin","short":"avad","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/avataradmin Administratoravatar hochladen oder ersetzen Lädt einen JPEG-, PNG- oder WebP-Avatar mit höchstens 8 MiB hoch. Der Eigentümer wird aus resource_id, dem Metadatenalias admin oder einer intern vorgegebenen Request-ID ermittelt; resource_type und filetype=avatar setzt der Server. Öffentlich unterstützte Multipart-Metadaten stehen im meta-JSON oder direkt in den Formfeldern resource_id/admin und filename; der POST-Transport verpackt diese serverseitig als data. Der Dateiname wird bei fehlendem filename vom Upload übernommen. Vorhandene Avatare des Eigentümers werden soft-gelöscht. Im Accountmodus face muss ein echtes Gesicht erkannt werden; symbol umgeht diese Gesichtspflicht. - Operation-ID: post_v2_avataradmin - Dokumentation: /docs/api/v2/operations/post-v2-avataradmin-9885df813842/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/avataradmin' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/avataradmin"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/avataradmin" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_avataradmin","summary":"Administratoravatar hochladen oder ersetzen","description":"Lädt einen JPEG-, PNG- oder WebP-Avatar mit höchstens 8 MiB hoch. Der Eigentümer wird aus resource_id, dem Metadatenalias admin oder einer intern vorgegebenen Request-ID ermittelt; resource_type und filetype=avatar setzt der Server. Öffentlich unterstützte Multipart-Metadaten stehen im meta-JSON oder direkt in den Formfeldern resource_id/admin und filename; der POST-Transport verpackt diese serverseitig als data. Der Dateiname wird bei fehlendem filename vom Upload übernommen. Vorhandene Avatare des Eigentümers werden soft-gelöscht. Im Accountmodus face muss ein echtes Gesicht erkannt werden; symbol umgeht diese Gesichtspflicht.","tags":["avataradmin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche files-ID des Avatars; auch PUT verwendet den Upload-/Einfügepfad und liefert insert.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Dateibytes. Avatare: JPEG/PNG/WebP, höchstens 8 MiB; andere Bilder höchstens 2 MiB, Angebotsanhänge höchstens 20 MiB."},"meta":{"type":["string","object"],"description":"JSON-Metadaten, direkt oder unter data verschachtelt. Unterstütztes Beispiel als JSON-Zeichenfolge.","examples":["{\"data\":{\"resource_id\":\"admin_012345abcdef_123456\",\"filename\":\"profil.png\"}}"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$"}],"description":"Öffentliche Eigentümer-ID bei direkten Multipart-Feldern."},"filename":{"type":"string"},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Alternativer direkter Eigentümerselektor."}},"required":["file"],"anyOf":[{"required":["meta"]},{"required":["resource_id"]},{"required":["admin"]}],"additionalProperties":true},"examples":{"upload":{"summary":"Synthetische Metadaten; file durch echte Dateibytes ersetzen.","value":{"file":"","meta":"{\"data\":{\"resource_id\":\"admin_012345abcdef_123456\",\"filename\":\"profil.png\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Avataradmin.php","method":"postAvataradmin","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"avataradmin","short":"avad","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/avataradmin/list/files/{fileId}/download Avatar- oder Administratordatei herunterladen Liefert die Datei über ihre öffentliche files-ID als Download, falls sie im Account vorhanden und nicht gelöscht ist. Diese Downloadfunktion prüft die Datei-ID und Accountzugehörigkeit; die Avatar-Filter des Aufrufers beschränken den direkten Downloadpfad nicht zusätzlich. Kundenspezifische Exportdateien bleiben ausgeschlossen; fehlende Dateien liefern einen JSON-Fehler. - Operation-ID: get_v2_avataradmin_list_files_fileId_download - Dokumentation: /docs/api/v2/operations/get-v2-avataradmin-list-files-fileid-download-4b127620db04/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fileId | path | ja | Öffentliche files-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fileId","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_FILEID:?PATH_FILEID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/avataradmin/list/files/'"${PATH_FILEID}"'/download' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/avataradmin/list/files/{fileId}/download"; path = path.replaceAll("{fileId}", encodeURIComponent(required("PATH_FILEID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/avataradmin/list/files/{fileId}/download" path = path.replace("{fileId}", urllib.parse.quote(os.environ["PATH_FILEID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_avataradmin_list_files_fileId_download","summary":"Avatar- oder Administratordatei herunterladen","description":"Liefert die Datei über ihre öffentliche files-ID als Download, falls sie im Account vorhanden und nicht gelöscht ist. Diese Downloadfunktion prüft die Datei-ID und Accountzugehörigkeit; die Avatar-Filter des Aufrufers beschränken den direkten Downloadpfad nicht zusätzlich. Kundenspezifische Exportdateien bleiben ausgeschlossen; fehlende Dateien liefern einen JSON-Fehler.","tags":["avataradmin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fileId","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Binärer Download mit Dateiname; bei fehlender Datei JSON-Fehler ED80100/file_not_found.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_code":{"type":"string"},"error_message":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true},"example":{"error":"ED80100","error_code":"file_not_found","error_message":"The requested file could not be found or has been deleted","type":"api"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Avataradmin.php","method":"getAvataradmin","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"avataradmin","short":"avad","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/avataruser Teilnehmeravatar entfernen Entfernt den ausgewählten Avatar. id darf entweder eine passende öffentliche files-ID oder eine user-ID sein; bei einer Eigentümer-ID wird der neueste nicht gelöschte Avatar dieses Eigentümers gesucht. Es werden ausschließlich Avatare des passenden Ressourcentyps und Accounts gelöscht. - Operation-ID: delete_v2_avataruser - Dokumentation: /docs/api/v2/operations/delete-v2-avataruser-23d4682cfd1d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["avataruser_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "user_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/avataruser' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/avataruser"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "user_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/avataruser" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"user_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_avataruser","summary":"Teilnehmeravatar entfernen","description":"Entfernt den ausgewählten Avatar. id darf entweder eine passende öffentliche files-ID oder eine user-ID sein; bei einer Eigentümer-ID wird der neueste nicht gelöschte Avatar dieses Eigentümers gesucht. Es werden ausschließlich Avatare des passenden Ressourcentyps und Accounts gelöscht.","tags":["avataruser"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["avataruser_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Normalisierte öffentliche files-ID und delete-Ergebnis; avatar_not_found bei fehlendem Avatar.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Teilnehmeravatar entfernen","value":{"id":"user_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Avataruser.php","method":"deleteAvataruser","line":49},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"avataruser","short":"avus","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/avataruser Teilnehmeravatar hochladen oder ersetzen Lädt einen JPEG-, PNG- oder WebP-Avatar mit höchstens 8 MiB hoch. Der Eigentümer wird aus resource_id, dem Metadatenalias user oder einer intern vorgegebenen Request-ID ermittelt; resource_type und filetype=avatar setzt der Server. Öffentlich unterstützte Multipart-Metadaten stehen im meta-JSON oder direkt in den Formfeldern resource_id/user und filename; der POST-Transport verpackt diese serverseitig als data. Der Dateiname wird bei fehlendem filename vom Upload übernommen. Vorhandene Avatare des Eigentümers werden soft-gelöscht. Der Teilnehmer muss zum ausgewählten Account gehören. - Operation-ID: post_v2_avataruser - Dokumentation: /docs/api/v2/operations/post-v2-avataruser-5c76305bebbc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/avataruser' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/avataruser"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/avataruser" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_avataruser","summary":"Teilnehmeravatar hochladen oder ersetzen","description":"Lädt einen JPEG-, PNG- oder WebP-Avatar mit höchstens 8 MiB hoch. Der Eigentümer wird aus resource_id, dem Metadatenalias user oder einer intern vorgegebenen Request-ID ermittelt; resource_type und filetype=avatar setzt der Server. Öffentlich unterstützte Multipart-Metadaten stehen im meta-JSON oder direkt in den Formfeldern resource_id/user und filename; der POST-Transport verpackt diese serverseitig als data. Der Dateiname wird bei fehlendem filename vom Upload übernommen. Vorhandene Avatare des Eigentümers werden soft-gelöscht. Der Teilnehmer muss zum ausgewählten Account gehören.","tags":["avataruser"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche files-ID des Avatars; auch PUT verwendet den Upload-/Einfügepfad und liefert insert.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Dateibytes. Avatare: JPEG/PNG/WebP, höchstens 8 MiB; andere Bilder höchstens 2 MiB, Angebotsanhänge höchstens 20 MiB."},"meta":{"type":["string","object"],"description":"JSON-Metadaten, direkt oder unter data verschachtelt. Unterstütztes Beispiel als JSON-Zeichenfolge.","examples":["{\"data\":{\"resource_id\":\"user_012345abcdef_123456\",\"filename\":\"profil.png\"}}"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$"}],"description":"Öffentliche Eigentümer-ID bei direkten Multipart-Feldern."},"filename":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Alternativer direkter Eigentümerselektor."}},"required":["file"],"anyOf":[{"required":["meta"]},{"required":["resource_id"]},{"required":["user"]}],"additionalProperties":true},"examples":{"upload":{"summary":"Synthetische Metadaten; file durch echte Dateibytes ersetzen.","value":{"file":"","meta":"{\"data\":{\"resource_id\":\"user_012345abcdef_123456\",\"filename\":\"profil.png\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Avataruser.php","method":"postAvataruser","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"avataruser","short":"avus","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/avataruser/list/files/{fileId}/download Teilnehmeravatar herunterladen Liefert die Datei über ihre öffentliche files-ID als Download, falls sie im Account vorhanden und nicht gelöscht ist. Diese Downloadfunktion prüft die Datei-ID und Accountzugehörigkeit; die Avatar-Filter des Aufrufers beschränken den direkten Downloadpfad nicht zusätzlich. Kundenspezifische Exportdateien bleiben ausgeschlossen; fehlende Dateien liefern einen JSON-Fehler. - Operation-ID: get_v2_avataruser_list_files_fileId_download - Dokumentation: /docs/api/v2/operations/get-v2-avataruser-list-files-fileid-download-ef897c3fa81b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fileId | path | ja | Öffentliche files-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fileId","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_FILEID:?PATH_FILEID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/avataruser/list/files/'"${PATH_FILEID}"'/download' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/avataruser/list/files/{fileId}/download"; path = path.replaceAll("{fileId}", encodeURIComponent(required("PATH_FILEID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/avataruser/list/files/{fileId}/download" path = path.replace("{fileId}", urllib.parse.quote(os.environ["PATH_FILEID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_avataruser_list_files_fileId_download","summary":"Teilnehmeravatar herunterladen","description":"Liefert die Datei über ihre öffentliche files-ID als Download, falls sie im Account vorhanden und nicht gelöscht ist. Diese Downloadfunktion prüft die Datei-ID und Accountzugehörigkeit; die Avatar-Filter des Aufrufers beschränken den direkten Downloadpfad nicht zusätzlich. Kundenspezifische Exportdateien bleiben ausgeschlossen; fehlende Dateien liefern einen JSON-Fehler.","tags":["avataruser"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fileId","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Binärer Download mit Dateiname; bei fehlender Datei JSON-Fehler ED80100/file_not_found.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"error_code":{"type":"string"},"error_message":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true},"example":{"error":"ED80100","error_code":"file_not_found","error_message":"The requested file could not be found or has been deleted","type":"api"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Avataruser.php","method":"getAvataruser","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"avataruser","short":"avus","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/barcode Teilnehmer-Barcode auflisten Liest Teilnehmer-Barcode-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. - Operation-ID: get_v2_barcode - Dokumentation: /docs/api/v2/operations/get-v2-barcode-fb143b2c9ae0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/barcode' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/barcode"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/barcode" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_barcode","summary":"Teilnehmer-Barcode auflisten","description":"Liest Teilnehmer-Barcode-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind [].","tags":["barcode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"barcode_012345abcdef_123456":{"id":"barcode_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},"status":{"type":"boolean"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},"type":{"type":"string"},"code":{"type":"string"},"date_create":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},"status":{"type":"boolean"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},"type":{"type":"string"},"code":{"type":"string"},"date_create":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Barcode.php","method":"getBarcode","line":172},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"barcode","short":"baco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/barcode Teilnehmer-Barcode erstellen Erstellt einen neuen Barcode für einen vorhandenen Teilnehmer; data.user ist erforderlich. Der Hook erzeugt Code und Barcode-Typ serverseitig, deaktiviert einen vorhandenen aktiven Code des Teilnehmers, sperrt ihn gegebenenfalls bei Dinaco und stößt die Zutrittssynchronisierung an. data kann ein Objekt oder eine Liste sein. - Operation-ID: post_v2_barcode - Dokumentation: /docs/api/v2/operations/post-v2-barcode-cd60a4822e54/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "user": "user_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/barcode' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/barcode"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "user": "user_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/barcode" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"user\":\"user_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_barcode","summary":"Teilnehmer-Barcode erstellen","description":"Erstellt einen neuen Barcode für einen vorhandenen Teilnehmer; data.user ist erforderlich. Der Hook erzeugt Code und Barcode-Typ serverseitig, deaktiviert einen vorhandenen aktiven Code des Teilnehmers, sperrt ihn gegebenenfalls bei Dinaco und stößt die Zutrittssynchronisierung an. data kann ein Objekt oder eine Liste sein.","tags":["barcode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält eine öffentliche ID oder bei Stapelerstellung eine ID-Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"barcode_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-canonicalType":"boolean","description":"Client status is initially normalized but generated barcode payload overwrites it with aktiv=1, even if false was supplied.\n\nPUT tests === false; all other values are successful no-ops. POST normalizes status but before-insert replaces the whole ticket with a generated active barcode.\n\nScope / limitations: The const false suggestion describes state-changing usage, not server rejection of other input.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Barcode.php","line":34,"lineStart":34,"lineEnd":44,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"efd08662ed3fea076495c5fe8baaa6667539a65606fdc6cccfde7378994419c3","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Barcodefunc.php","line":50,"lineStart":50,"lineEnd":68,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"d7dc8832253da87f0914d8496a38c440719722372a21585a32c121ed38e9ab37","reason":"func delegates to _statusBarcode"},{"file":"application/models/v2/Barcodefunc.php","line":5,"lineStart":5,"lineEnd":48,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"be04e60dcd2dd2845f50b478d4be2197bfb7514947eda2149da521dc30c50f6f","reason":"Before-insert replaces DataUP with creatBarcode(...,true)."},{"file":"application/helpers/edooboxhelp_helper.php","line":10235,"lineStart":10235,"lineEnd":10246,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"45d1b149836f592b42cb0d09bd52d3da3ba5c5c34968d8721f72b40109b0864b","reason":"Generated barcode sets aktiv=1 and creation time."},{"file":"application/models/v2/Barcode.php","line":125,"lineStart":125,"lineEnd":148,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"2711bb5f591b0fbbfc48d612e2c7eccfe586c39e7db5efd36171d115afbbc9bf","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":45},"type":{"x-edoobox-knownInputs":["pdf417","qrcode","datamatrix"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["pdf417"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":59},"code":{"type":"string","description":"String is split at X and CRC prefix checked; bad checksum normalizes empty, not rejection. Before-insert discards caller code and generates actual ticket.\n\nWrong CRC/missing X -> empty text. Good prefix -> aP(base,true). Before-insert replaces either result with generated ticket_code.\n\nScope / limitations: Prefer response-only documentation; supplied code has no effect on successful ticket creation. No checksum regex should be described as a rejecting request validator.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Barcode.php","line":81,"lineStart":81,"lineEnd":102,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"d13d05149b295ff5b393dbd3caaf36f24906f342f821602e302e5cb92796ca29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Barcodefunc.php","line":5,"lineStart":5,"lineEnd":48,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"be04e60dcd2dd2845f50b478d4be2197bfb7514947eda2149da521dc30c50f6f","reason":"Fresh generated ticket replaces parsed client code."},{"file":"application/helpers/edooboxhelp_helper.php","line":10209,"lineStart":10209,"lineEnd":10246,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"36a6d3fd64229dc71df236319f26d7262d6590b82a2471fc2b1653a7da66dd90","reason":"Unique generated base code and CRC-prefix output suffix."},{"file":"application/models/v2/Barcode.php","line":125,"lineStart":125,"lineEnd":148,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"2711bb5f591b0fbbfc48d612e2c7eccfe586c39e7db5efd36171d115afbbc9bf","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_create":{"readOnly":true,"description":"Getter ignores input and returns time(). insert_default injects it on omission when present; omission otherwise does not call the getter. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Barcode.php","line":103,"lineStart":103,"lineEnd":113,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"41dbac58a999ae075b4ceba7219f7c44eb67f8d3b00292632bb5c1e0dde652cf","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Barcodefunc.php","line":5,"lineStart":5,"lineEnd":48,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"be04e60dcd2dd2845f50b478d4be2197bfb7514947eda2149da521dc30c50f6f","reason":"Before-insert replaces entire payload with generated ticket."},{"file":"application/helpers/edooboxhelp_helper.php","line":10235,"lineStart":10235,"lineEnd":10246,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"45d1b149836f592b42cb0d09bd52d3da3ba5c5c34968d8721f72b40109b0864b","reason":"Generated payload sets create_time=time()."},{"file":"application/models/v2/Barcode.php","line":125,"lineStart":125,"lineEnd":148,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"2711bb5f591b0fbbfc48d612e2c7eccfe586c39e7db5efd36171d115afbbc9bf","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["user"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-canonicalType":"boolean","description":"Client status is initially normalized but generated barcode payload overwrites it with aktiv=1, even if false was supplied.\n\nPUT tests === false; all other values are successful no-ops. POST normalizes status but before-insert replaces the whole ticket with a generated active barcode.\n\nScope / limitations: The const false suggestion describes state-changing usage, not server rejection of other input.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Barcode.php","line":34,"lineStart":34,"lineEnd":44,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"efd08662ed3fea076495c5fe8baaa6667539a65606fdc6cccfde7378994419c3","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Barcodefunc.php","line":50,"lineStart":50,"lineEnd":68,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"d7dc8832253da87f0914d8496a38c440719722372a21585a32c121ed38e9ab37","reason":"func delegates to _statusBarcode"},{"file":"application/models/v2/Barcodefunc.php","line":5,"lineStart":5,"lineEnd":48,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"be04e60dcd2dd2845f50b478d4be2197bfb7514947eda2149da521dc30c50f6f","reason":"Before-insert replaces DataUP with creatBarcode(...,true)."},{"file":"application/helpers/edooboxhelp_helper.php","line":10235,"lineStart":10235,"lineEnd":10246,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"45d1b149836f592b42cb0d09bd52d3da3ba5c5c34968d8721f72b40109b0864b","reason":"Generated barcode sets aktiv=1 and creation time."},{"file":"application/models/v2/Barcode.php","line":125,"lineStart":125,"lineEnd":148,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"2711bb5f591b0fbbfc48d612e2c7eccfe586c39e7db5efd36171d115afbbc9bf","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":45},"type":{"x-edoobox-knownInputs":["pdf417","qrcode","datamatrix"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["pdf417"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":59},"code":{"type":"string","description":"String is split at X and CRC prefix checked; bad checksum normalizes empty, not rejection. Before-insert discards caller code and generates actual ticket.\n\nWrong CRC/missing X -> empty text. Good prefix -> aP(base,true). Before-insert replaces either result with generated ticket_code.\n\nScope / limitations: Prefer response-only documentation; supplied code has no effect on successful ticket creation. No checksum regex should be described as a rejecting request validator.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Barcode.php","line":81,"lineStart":81,"lineEnd":102,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"d13d05149b295ff5b393dbd3caaf36f24906f342f821602e302e5cb92796ca29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Barcodefunc.php","line":5,"lineStart":5,"lineEnd":48,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"be04e60dcd2dd2845f50b478d4be2197bfb7514947eda2149da521dc30c50f6f","reason":"Fresh generated ticket replaces parsed client code."},{"file":"application/helpers/edooboxhelp_helper.php","line":10209,"lineStart":10209,"lineEnd":10246,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"36a6d3fd64229dc71df236319f26d7262d6590b82a2471fc2b1653a7da66dd90","reason":"Unique generated base code and CRC-prefix output suffix."},{"file":"application/models/v2/Barcode.php","line":125,"lineStart":125,"lineEnd":148,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"2711bb5f591b0fbbfc48d612e2c7eccfe586c39e7db5efd36171d115afbbc9bf","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_create":{"readOnly":true,"description":"Getter ignores input and returns time(). insert_default injects it on omission when present; omission otherwise does not call the getter. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Barcode.php","line":103,"lineStart":103,"lineEnd":113,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"41dbac58a999ae075b4ceba7219f7c44eb67f8d3b00292632bb5c1e0dde652cf","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Barcodefunc.php","line":5,"lineStart":5,"lineEnd":48,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"be04e60dcd2dd2845f50b478d4be2197bfb7514947eda2149da521dc30c50f6f","reason":"Before-insert replaces entire payload with generated ticket."},{"file":"application/helpers/edooboxhelp_helper.php","line":10235,"lineStart":10235,"lineEnd":10246,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"45d1b149836f592b42cb0d09bd52d3da3ba5c5c34968d8721f72b40109b0864b","reason":"Generated payload sets create_time=time()."},{"file":"application/models/v2/Barcode.php","line":125,"lineStart":125,"lineEnd":148,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"2711bb5f591b0fbbfc48d612e2c7eccfe586c39e7db5efd36171d115afbbc9bf","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["user"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["barcode_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Teilnehmer-Barcode erstellen","value":{"data":{"user":"user_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Barcode.php","method":"postBarcode","line":125},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"barcode","short":"baco","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/barcode Teilnehmer-Barcode ändern Deaktiviert den Teilnehmer-Barcode ausschließlich bei data.status=false (JSON boolean). Andere Werte bestätigen update=false; eine Reaktivierung erfolgt hier nicht. user, type, code und date_create sind unveränderbar. Die v2-ID muss als id im JSON-Envelope stehen. - Operation-ID: put_v2_barcode - Dokumentation: /docs/api/v2/operations/put-v2-barcode-f7ce21241a56/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false }, "id": "barcode_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/barcode' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/barcode"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false }, "id": "barcode_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/barcode" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false},\"id\":\"barcode_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_barcode","summary":"Teilnehmer-Barcode ändern","description":"Deaktiviert den Teilnehmer-Barcode ausschließlich bei data.status=false (JSON boolean). Andere Werte bestätigen update=false; eine Reaktivierung erfolgt hier nicht. user, type, code und date_create sind unveränderbar. Die v2-ID muss als id im JSON-Envelope stehen.","tags":["barcode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"barcode_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-canonicalType":"boolean","x-edoobox-stateChangingValue":false,"description":"Literal false deactivates. Every other input returns success update=false and cannot reactivate. No rejecting wire boolean/const restriction.\n\nPUT tests === false; all other values are successful no-ops. POST normalizes status but before-insert replaces the whole ticket with a generated active barcode.\n\nScope / limitations: The const false suggestion describes state-changing usage, not server rejection of other input.","x-edoobox-schemaStatus":"source-reviewed","examples":[false],"x-edoobox-contractSource":[{"file":"application/models/v2/Barcode.php","line":34,"lineStart":34,"lineEnd":44,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"efd08662ed3fea076495c5fe8baaa6667539a65606fdc6cccfde7378994419c3","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Barcodefunc.php","line":50,"lineStart":50,"lineEnd":68,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"d7dc8832253da87f0914d8496a38c440719722372a21585a32c121ed38e9ab37","reason":"func delegates to _statusBarcode"},{"file":"application/models/v2/Barcodefunc.php","line":5,"lineStart":5,"lineEnd":48,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"be04e60dcd2dd2845f50b478d4be2197bfb7514947eda2149da521dc30c50f6f","reason":"Before-insert replaces DataUP with creatBarcode(...,true)."},{"file":"application/helpers/edooboxhelp_helper.php","line":10235,"lineStart":10235,"lineEnd":10246,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"45d1b149836f592b42cb0d09bd52d3da3ba5c5c34968d8721f72b40109b0864b","reason":"Generated barcode sets aktiv=1 and creation time."},{"file":"application/models/v2/Barcode.php","line":125,"lineStart":125,"lineEnd":148,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"2711bb5f591b0fbbfc48d612e2c7eccfe586c39e7db5efd36171d115afbbc9bf","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"user":false,"type":false,"code":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["user","type","code","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["barcode_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Teilnehmer-Barcode ändern","value":{"data":{"status":false},"id":"barcode_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Barcode.php","method":"updateBarcode","line":141},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"barcode","short":"baco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/barcode/{id} Teilnehmer-Barcode lesen Liest Teilnehmer-Barcode anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. - Operation-ID: get_v2_barcode_id - Dokumentation: /docs/api/v2/operations/get-v2-barcode-id-f145b1cab85c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["barcode_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/barcode/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/barcode/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/barcode/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_barcode_id","summary":"Teilnehmer-Barcode lesen","description":"Liest Teilnehmer-Barcode anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind [].","tags":["barcode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["barcode_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"barcode_012345abcdef_123456":{"id":"barcode_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},"status":{"type":"boolean"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},"type":{"type":"string"},"code":{"type":"string"},"date_create":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},"status":{"type":"boolean"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},"type":{"type":"string"},"code":{"type":"string"},"date_create":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Barcode.php","method":"getBarcode","line":172},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"barcode","short":"baco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/barcode/{id} Teilnehmer-Barcode ändern Deaktiviert den Teilnehmer-Barcode ausschließlich bei data.status=false (JSON boolean). Andere Werte bestätigen update=false; eine Reaktivierung erfolgt hier nicht. user, type, code und date_create sind unveränderbar. Die v2-ID steht im Pfad. - Operation-ID: put_v2_barcode_id - Dokumentation: /docs/api/v2/operations/put-v2-barcode-id-f39f18ebfcf7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["barcode_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/barcode/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/barcode/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/barcode/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_barcode_id","summary":"Teilnehmer-Barcode ändern","description":"Deaktiviert den Teilnehmer-Barcode ausschließlich bei data.status=false (JSON boolean). Andere Werte bestätigen update=false; eine Reaktivierung erfolgt hier nicht. user, type, code und date_create sind unveränderbar. Die v2-ID steht im Pfad.","tags":["barcode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["barcode_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"barcode_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-canonicalType":"boolean","x-edoobox-stateChangingValue":false,"description":"Literal false deactivates. Every other input returns success update=false and cannot reactivate. No rejecting wire boolean/const restriction.\n\nPUT tests === false; all other values are successful no-ops. POST normalizes status but before-insert replaces the whole ticket with a generated active barcode.\n\nScope / limitations: The const false suggestion describes state-changing usage, not server rejection of other input.","x-edoobox-schemaStatus":"source-reviewed","examples":[false],"x-edoobox-contractSource":[{"file":"application/models/v2/Barcode.php","line":34,"lineStart":34,"lineEnd":44,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"efd08662ed3fea076495c5fe8baaa6667539a65606fdc6cccfde7378994419c3","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Barcodefunc.php","line":50,"lineStart":50,"lineEnd":68,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"d7dc8832253da87f0914d8496a38c440719722372a21585a32c121ed38e9ab37","reason":"func delegates to _statusBarcode"},{"file":"application/models/v2/Barcodefunc.php","line":5,"lineStart":5,"lineEnd":48,"sha256":"de066db69e74d57fffc5d9e6ae9590e3736edf042079f8cbf67c1e8ca1ea5950","spanSha256":"be04e60dcd2dd2845f50b478d4be2197bfb7514947eda2149da521dc30c50f6f","reason":"Before-insert replaces DataUP with creatBarcode(...,true)."},{"file":"application/helpers/edooboxhelp_helper.php","line":10235,"lineStart":10235,"lineEnd":10246,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"45d1b149836f592b42cb0d09bd52d3da3ba5c5c34968d8721f72b40109b0864b","reason":"Generated barcode sets aktiv=1 and creation time."},{"file":"application/models/v2/Barcode.php","line":125,"lineStart":125,"lineEnd":148,"sha256":"93074076026f5c29f8251a52a176e9a0be03078172392e51a188e04f24a56900","spanSha256":"2711bb5f591b0fbbfc48d612e2c7eccfe586c39e7db5efd36171d115afbbc9bf","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"user":false,"type":false,"code":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["user","type","code","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["barcode_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Teilnehmer-Barcode ändern","value":{"data":{"status":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Barcode.php","method":"updateBarcode","line":141},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"barcode","short":"baco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/barcode/list Teilnehmer-Barcode auflisten Liest Teilnehmer-Barcode-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. - Operation-ID: get_v2_barcode_list - Dokumentation: /docs/api/v2/operations/get-v2-barcode-list-df9c53f1af55/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/barcode/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/barcode/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/barcode/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_barcode_list","summary":"Teilnehmer-Barcode auflisten","description":"Liest Teilnehmer-Barcode-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind [].","tags":["barcode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"barcode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"barcode_012345abcdef_123456":{"id":"barcode_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},"status":{"type":"boolean"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},"type":{"type":"string"},"code":{"type":"string"},"date_create":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["barcode_012345abcdef_123456"]},"status":{"type":"boolean"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},"type":{"type":"string"},"code":{"type":"string"},"date_create":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Barcode.php","method":"getBarcode","line":172},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"barcode","short":"baco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking Buchungen und Buchungsstatus lesen Liest Buchungen des ausgewählten Kontos mit Angebot, Buchungsinhaber, Wartelisten-/Stornierungsstatus und optionalen Transaktionen beziehungsweise Teilnehmern. Eine Pfad-ID wird als erster id-Filter behandelt; ohne Pfad-ID begrenzen filter, fields, order und limit die Sammlung. Auch eine einzelne ID liefert das allgemeine Sammlungsformat. - Operation-ID: get_v2_booking - Dokumentation: /docs/api/v2/operations/get-v2-booking-92084f0c9a6f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking","summary":"Buchungen und Buchungsstatus lesen","description":"Liest Buchungen des ausgewählten Kontos mit Angebot, Buchungsinhaber, Wartelisten-/Stornierungsstatus und optionalen Transaktionen beziehungsweise Teilnehmern. Eine Pfad-ID wird als erster id-Filter behandelt; ohne Pfad-ID begrenzen filter, fields, order und limit die Sammlung. Auch eine einzelne ID liefert das allgemeine Sammlungsformat.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"booking_012345abcdef_123456":{"id":"booking_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/booking Buchung für einen bestehenden Kunden erstellen Erstellt genau eine Buchung für owner und offer. Die gewählte oder standardmässige Preiskategorie, Wartelistenfähigkeit, Mitgliedschaft und Kapazität werden im Konto geprüft; eine Buchung kann Abrechnung und weitere Buchungsfolgen auslösen. noreturn=true lässt die erweiterten Rückgaben booking und booking_data weg. Die bereits geprüften Aktionsschemas beschreiben die unterstützten Varianten. - Operation-ID: post_v2_booking - Dokumentation: /docs/api/v2/operations/post-v2-booking-8bed8fe40cb6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "owner": "user_012345abcdef_123456", "offer": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/booking' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "owner": "user_012345abcdef_123456", "offer": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"owner\":\"user_012345abcdef_123456\",\"offer\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_booking","summary":"Buchung für einen bestehenden Kunden erstellen","description":"Erstellt genau eine Buchung für owner und offer. Die gewählte oder standardmässige Preiskategorie, Wartelistenfähigkeit, Mitgliedschaft und Kapazität werden im Konto geprüft; eine Buchung kann Abrechnung und weitere Buchungsfolgen auslösen. noreturn=true lässt die erweiterten Rückgaben booking und booking_data weg. Die bereits geprüften Aktionsschemas beschreiben die unterstützten Varianten.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque participant v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["participant_012345abcdef_123456"]},{"const":false}]},"position":{"const":"person"},"booking_data":{"type":"object","properties":{},"additionalProperties":true,"description":"Dynamic expanded booking view, included unless noreturn is true."},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Dynamic source Structure readback, included unless noreturn is true."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true},"examples":{"created":{"value":{"error":false,"type":"booking","data":{"insert":true,"id":"booking_012345abcdef_123456","participant":"participant_012345abcdef_123456","position":"person"}}},"missingOwner":{"value":{"error":"ED80100","type":"booking","require":{"owner":false,"offer":false}}},"invalidPrice":{"value":{"error":"ED80100","error_code":"invalid_pricecategory","type":"booking"}}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Existing offer in the selected account; required by the native booking-create hook.\n\nExisting opaque offer v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["offer_012345abcdef_123456"],"x-edoobox-sourceLine":43},"owner":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nExisting opaque user v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["user_012345abcdef_123456"],"x-edoobox-sourceLine":58},"waiting_list":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":93,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false],"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking."},"pricecategory":{"anyOf":[{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque pricecategory v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["pricecategory_012345abcdef_123456"]},{"enum":[false,null,""]}],"description":"Optional explicit price selection. False, null and empty text use the native edisset/default-price path; the existing Vue booking form can submit an empty price selection."}},"additionalProperties":true,"description":"Create one booking for an existing owner and offer. Unknown fields remain open because the legacy hook tolerates additional client context.","required":["offer","owner"]},"noreturn":{"x-edoobox-canonicalType":"boolean","description":"PHP loose comparison controls whether booking_data and booking readback are appended; true omits the expensive expanded readback."}},"additionalProperties":true},"examples":{"minimal":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"owner":"user_012345abcdef_123456","offer":"offer_012345abcdef_123456"}}},"selectedPrice":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"owner":"user_012345abcdef_123456","offer":"offer_012345abcdef_123456","pricecategory":"pricecategory_012345abcdef_123456","waiting_list":false}}},"waitingList":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"owner":"user_012345abcdef_123456","offer":"offer_012345abcdef_123456","waiting_list":true}}},"defaultPrice":{"summary":"Existing client empty-selection variant chooses the native default price.","value":{"data":{"owner":"user_012345abcdef_123456","offer":"offer_012345abcdef_123456","pricecategory":""}}}}}}},"x-edoobox-action-contract":{"review":"booking-create","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Booking.php","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","methods":["__construct","postBooking"]},{"file":"application/models/v2/Bookingfunc.php","sha256":"de555a91da81398e3402d431d1dbda26eb1bc17019b2e44f17e854ced79762b7","methods":["_addBooking"]},{"file":"application/helpers/edooboxhelp_helper.php","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","symbols":["edisset"],"reason":"Falsy optional price selections fall through to the default-price lookup."}],"preconditions":["Owner and offer must exist in the selected account. Child offers resolve through their parent booking.","A matching explicit price or exactly one active default price must be available; a missing/nonmatching explicit selection can fall back to the default.","Waiting-list bookings cannot be created for lesson-based offers. Membership eligibility, balance reservation and invoicing run in the native hook."],"errors":[{"error":"ED80100","meaning":"Missing/invalid owner or failed native write/lock."},{"error":"ED80020","meaning":"Owner/offer relation or parent offer was not found in this account."},{"error":"ED80100","error_code":"invalid_pricecategory","meaning":"No usable default price could be selected."},{"error":"ED90030","meaning":"Waiting-list mode conflicts with lesson-based registration."}],"unresolved":["Database existence, signed ID integrity, membership eligibility, dynamic price/account settings and downstream side effects remain native checks.","Expanded booking_data and booking responses are intentionally open.","The native early Require_Data guard repeats owner; offer is nevertheless consumed and checked by the subsequent account-scoped offer query. This contract does not claim that early guard validates both fields."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"postBooking","line":170},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking Interne Referenz einer Buchung ändern Ändert das Feld label einer vorhandenen Buchung; aP normalisiert und begrenzt die gespeicherte Referenz auf 15 Zeichen. Angebot, Inhaber, Teilnehmer, Wartelisten- und Stornierungsstatus werden über eigene Aktionen geändert. Ohne Pfad-ID muss die Buchungs-ID im obersten Feld id stehen, nicht in data.id. - Operation-ID: put_v2_booking - Dokumentation: /docs/api/v2/operations/put-v2-booking-8b2174b0e115/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "label": "Kursgruppe A" }, "id": "booking_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "label": "Kursgruppe A" }, "id": "booking_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"label\":\"Kursgruppe A\"},\"id\":\"booking_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking","summary":"Interne Referenz einer Buchung ändern","description":"Ändert das Feld label einer vorhandenen Buchung; aP normalisiert und begrenzt die gespeicherte Referenz auf 15 Zeichen. Angebot, Inhaber, Teilnehmer, Wartelisten- und Stornierungsstatus werden über eigene Aktionen geändert. Ohne Pfad-ID muss die Buchungs-ID im obersten Feld id stehen, nicht in data.id.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"time":false,"offer":false,"owner":false,"canceled":false,"mustpay":false,"waiting_list":false,"label":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":15,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":103},"users":false,"transactions":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["time","offer","owner","canceled","mustpay","waiting_list","users","transactions"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Interne Referenz einer Buchung ändern","value":{"data":{"label":"Kursgruppe A"},"id":"booking_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id} Buchungen und Buchungsstatus lesen Liest Buchungen des ausgewählten Kontos mit Angebot, Buchungsinhaber, Wartelisten-/Stornierungsstatus und optionalen Transaktionen beziehungsweise Teilnehmern. Eine Pfad-ID wird als erster id-Filter behandelt; ohne Pfad-ID begrenzen filter, fields, order und limit die Sammlung. Auch eine einzelne ID liefert das allgemeine Sammlungsformat. - Operation-ID: get_v2_booking_id - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-3ff9b7d1c5a2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id","summary":"Buchungen und Buchungsstatus lesen","description":"Liest Buchungen des ausgewählten Kontos mit Angebot, Buchungsinhaber, Wartelisten-/Stornierungsstatus und optionalen Transaktionen beziehungsweise Teilnehmern. Eine Pfad-ID wird als erster id-Filter behandelt; ohne Pfad-ID begrenzen filter, fields, order und limit die Sammlung. Auch eine einzelne ID liefert das allgemeine Sammlungsformat.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"booking_012345abcdef_123456":{"id":"booking_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id} Interne Referenz einer Buchung ändern Ändert das Feld label einer vorhandenen Buchung; aP normalisiert und begrenzt die gespeicherte Referenz auf 15 Zeichen. Angebot, Inhaber, Teilnehmer, Wartelisten- und Stornierungsstatus werden über eigene Aktionen geändert. Ohne Pfad-ID muss die Buchungs-ID im obersten Feld id stehen, nicht in data.id. - Operation-ID: put_v2_booking_id - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-da0e82cdb7e7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "label": "Kursgruppe A" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "label": "Kursgruppe A" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"label\":\"Kursgruppe A\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id","summary":"Interne Referenz einer Buchung ändern","description":"Ändert das Feld label einer vorhandenen Buchung; aP normalisiert und begrenzt die gespeicherte Referenz auf 15 Zeichen. Angebot, Inhaber, Teilnehmer, Wartelisten- und Stornierungsstatus werden über eigene Aktionen geändert. Ohne Pfad-ID muss die Buchungs-ID im obersten Feld id stehen, nicht in data.id.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"time":false,"offer":false,"owner":false,"canceled":false,"mustpay":false,"waiting_list":false,"label":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":15,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":103},"users":false,"transactions":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["time","offer","owner","canceled","mustpay","waiting_list","users","transactions"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Interne Referenz einer Buchung ändern","value":{"data":{"label":"Kursgruppe A"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/add_participation Wartelistenbuchung als Teilnahme übernehmen Übernimmt eine Wartelisten-/MustPay-Buchung in das angegebene offer; ein anderes Zielangebot kann eine Umbuchung beziehungsweise Kopie auslösen. copy muss explizit vorhanden sein. Mit Pfadwert batch verarbeitet data.booking mehrere öffentliche Buchungs-IDs. Die Rückgabe data.id ist eine Liste der bearbeiteten Buchungs-IDs. - Operation-ID: put_v2_booking_id_add_participation - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-add-participation-5c348bca800a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Buchungs-ID oder batch; bei batch ist data.booking erforderlich. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Buchungs-ID oder batch; bei batch ist data.booking erforderlich.","schema":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},{"const":"batch"}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "copy": false } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/add_participation' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/add_participation"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "copy": false } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/add_participation" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"copy\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_add_participation","summary":"Wartelistenbuchung als Teilnahme übernehmen","description":"Übernimmt eine Wartelisten-/MustPay-Buchung in das angegebene offer; ein anderes Zielangebot kann eine Umbuchung beziehungsweise Kopie auslösen. copy muss explizit vorhanden sein. Mit Pfadwert batch verarbeitet data.booking mehrere öffentliche Buchungs-IDs. Die Rückgabe data.id ist eine Liste der bearbeiteten Buchungs-IDs.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Buchungs-ID oder batch; bei batch ist data.booking erforderlich.","schema":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},{"const":"batch"}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":["booking_012345abcdef_123456"],"update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"array","items":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]}},"update":{"type":"boolean"},"send_to":{"type":"object","additionalProperties":{"type":"boolean"}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"minItems":1},{"type":"object","additionalProperties":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"minProperties":1}]},"copy":{"x-edoobox-canonicalType":"boolean","description":"Die native PHP-Wahrheitsauswertung normalisiert den Wert; true/false sind die kanonische JSON-Eingabe."}},"additionalProperties":true,"required":["offer","copy"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Wartelistenbuchung als Teilnahme übernehmen","value":{"data":{"offer":"offer_012345abcdef_123456","copy":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":440}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/ask_participation Teilnahmeangebot per E-Mail versenden Sendet dem Buchungsinhaber eine Anfrage zur Teilnahme am angegebenen offer. Mit Pfadwert batch verarbeitet data.booking mehrere Buchungs-IDs. data.send_to ordnet jeder bearbeiteten öffentlichen Buchungs-ID den Versanderfolg zu; update=false ist hier beabsichtigt, da die Teilnahme noch nicht bestätigt wird. - Operation-ID: put_v2_booking_id_ask_participation - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-ask-participation-e48b40daa93d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Buchungs-ID oder batch; bei batch ist data.booking erforderlich. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Buchungs-ID oder batch; bei batch ist data.booking erforderlich.","schema":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},{"const":"batch"}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/ask_participation' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/ask_participation"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/ask_participation" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_ask_participation","summary":"Teilnahmeangebot per E-Mail versenden","description":"Sendet dem Buchungsinhaber eine Anfrage zur Teilnahme am angegebenen offer. Mit Pfadwert batch verarbeitet data.booking mehrere Buchungs-IDs. data.send_to ordnet jeder bearbeiteten öffentlichen Buchungs-ID den Versanderfolg zu; update=false ist hier beabsichtigt, da die Teilnahme noch nicht bestätigt wird.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Buchungs-ID oder batch; bei batch ist data.booking erforderlich.","schema":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},{"const":"batch"}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":false,"send_to":{"booking_012345abcdef_123456":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},{"const":"batch"}]},"update":{"type":"boolean"},"send_to":{"type":"object","additionalProperties":{"type":"boolean"}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"minItems":1},{"type":"object","additionalProperties":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"minProperties":1}]}},"additionalProperties":true,"required":["offer"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Teilnahmeangebot per E-Mail versenden","value":{"data":{"offer":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":445}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/booking.pdf Buchungsbestätigung als PDF laden Rendert das zu dieser Buchung konfigurierte PDF und gibt dessen Bytes direkt zurück. Die Pfad-ID wählt eine Buchung des Kontos; ein optionales cacheToken ist nur ein zusätzlicher URL-Bestandteil und wird vom Handler nicht ausgewertet. Der Dateiaufruf versendet keine Bestätigungs-E-Mail. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden. - Operation-ID: get_v2_booking_id_booking_pdf - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-booking-pdf-bf1ce27daf36/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/booking.pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/booking.pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/booking.pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_booking_pdf","summary":"Buchungsbestätigung als PDF laden","description":"Rendert das zu dieser Buchung konfigurierte PDF und gibt dessen Bytes direkt zurück. Die Pfad-ID wählt eine Buchung des Kontos; ein optionales cacheToken ist nur ein zusätzlicher URL-Bestandteil und wird vom Handler nicht ausgewertet. Der Dateiaufruf versendet keine Bestätigungs-E-Mail. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Direkt gerendertes PDF; kontoabhängige Sichtbarkeit und Vorlagen bestimmen den Inhalt. Bei ungültigen Selektoren ist stattdessen eine JSON-Fehlerantwort möglich.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":867}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/booking.pdf/{cacheToken} Buchungsbestätigung als PDF laden Rendert das zu dieser Buchung konfigurierte PDF und gibt dessen Bytes direkt zurück. Die Pfad-ID wählt eine Buchung des Kontos; ein optionales cacheToken ist nur ein zusätzlicher URL-Bestandteil und wird vom Handler nicht ausgewertet. Der Dateiaufruf versendet keine Bestätigungs-E-Mail. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden. - Operation-ID: get_v2_booking_id_booking_pdf_cacheToken - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-booking-pdf-cachetoken-af5a65517175/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | | | cacheToken | path | ja | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque booking v2 ID returned by this account. Synthetic example is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${PATH_CACHETOKEN:?PATH_CACHETOKEN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/booking.pdf/'"${PATH_CACHETOKEN}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/booking.pdf/{cacheToken}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); path = path.replaceAll("{cacheToken}", encodeURIComponent(required("PATH_CACHETOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/booking.pdf/{cacheToken}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) path = path.replace("{cacheToken}", urllib.parse.quote(os.environ["PATH_CACHETOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_booking_pdf_cacheToken","summary":"Buchungsbestätigung als PDF laden","description":"Rendert das zu dieser Buchung konfigurierte PDF und gibt dessen Bytes direkt zurück. Die Pfad-ID wählt eine Buchung des Kontos; ein optionales cacheToken ist nur ein zusätzlicher URL-Bestandteil und wird vom Handler nicht ausgewertet. Der Dateiaufruf versendet keine Bestätigungs-E-Mail. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque booking v2 ID returned by this account. Synthetic example is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}],"responses":{"200":{"description":"Direkt gerendertes PDF; kontoabhängige Sichtbarkeit und Vorlagen bestimmen den Inhalt. Bei ungültigen Selektoren ist stattdessen eine JSON-Fehlerantwort möglich.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Booking.php","line":867,"reason":"The explicit PDF branch uses the booking ID filter and does not consume the reviewed final cAe timestamp cache segment."},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/8","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":867,"lineEnd":906,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"Account-scoped booking SELECT feeds shop_offerte::sendBestatigungsMail2 with Output File and returns/exits."},"reason":"Renders an existing booking confirmation as a PDF; the misleadingly named helper is the PDF-only renderer.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/8","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":867}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/cancel Gesamte Buchung stornieren Storniert die Buchung samt zugehörigen Teilnehmer- und Kontobewegungen. Hinterlegte Stornierungsbedingungen können Gebühren, Rückerstattungen, Rechnungsanpassungen und Bestätigungen auslösen. cancellation_fee_mode wählt Regeln, Erlass oder einen individuellen Betrag. update=false sowie die Rückgabefelder zu Abrechnungssperren und manueller Prüfung bedeuten, dass keine erfolgreiche Stornierung bestätigt wurde. - Operation-ID: put_v2_booking_id_cancel - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-cancel-cc0b0bda3f27/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "cancellation_fee_mode": "rules" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/cancel' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/cancel"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "cancellation_fee_mode": "rules" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/cancel" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"cancellation_fee_mode\":\"rules\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_cancel","summary":"Gesamte Buchung stornieren","description":"Storniert die Buchung samt zugehörigen Teilnehmer- und Kontobewegungen. Hinterlegte Stornierungsbedingungen können Gebühren, Rückerstattungen, Rechnungsanpassungen und Bestätigungen auslösen. cancellation_fee_mode wählt Regeln, Erlass oder einen individuellen Betrag. update=false sowie die Rückgabefelder zu Abrechnungssperren und manueller Prüfung bedeuten, dass keine erfolgreiche Stornierung bestätigt wurde.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true,"cancellation_conditions":false,"fee":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"cancel":{"type":"boolean"},"cancellation_conditions":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"const":false}]},"fee":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Gebühren-/Refundergebnis oder Blockierungsgrund; abhängig von Stornierungsregel und Abrechnung."},{"const":false}]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["paytrans_012345abcdef_123456"]},"accounting_operation_in_progress":{"type":"boolean"},"accounting_manual_review":{"type":"boolean"},"automatic_refund_blocked":{"type":"boolean"},"invoice_cancellation_blocked":{"type":"boolean"},"reason":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"cancellation_fee_mode":{"description":"Mit string normalisiert. rules (Standard), waive oder custom; alle anderen Werte fallen auf rules zurück.","examples":["rules","waive","custom"],"x-edoobox-canonicalType":"string"},"cancellation_fee_amount":{"x-edoobox-canonicalType":"number","description":"Bei custom als float normalisiert; Betrag in der Buchungswährung."},"cancellation_fee_reason":{"type":["string","number","boolean","null"]}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Gesamte Buchung stornieren","value":{"data":{"cancellation_fee_mode":"rules"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":414}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/cancellation_condition Stornierungsbedingungen einer Buchung ändern Übernimmt eine cancellationconditiongroup als neuen Snapshot in die Buchung, aktualisiert einen bestehenden Widerrufsverzicht oder entfernt den Snapshot. group, group_id und cancellation_condition_group sind alternative Gruppenselektoren. /remove oder remove=true entfernt; ignore_withdrawal beziehungsweise withdrawal_ignore aktualisiert den Verzicht. Änderungen werden mit Historie und Notiz protokolliert; ohne neuen Zustand liefert update=false den unveränderten Snapshot. - Operation-ID: put_v2_booking_id_cancellation_condition - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-cancellation-condition-733b3cc51f07/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "group": "cancellationconditiongroup_012345abcdef_123456", "reason": "Manuelle Vereinbarung" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/cancellation_condition' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/cancellation_condition"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "group": "cancellationconditiongroup_012345abcdef_123456", "reason": "Manuelle Vereinbarung" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/cancellation_condition" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"group\":\"cancellationconditiongroup_012345abcdef_123456\",\"reason\":\"Manuelle Vereinbarung\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_cancellation_condition","summary":"Stornierungsbedingungen einer Buchung ändern","description":"Übernimmt eine cancellationconditiongroup als neuen Snapshot in die Buchung, aktualisiert einen bestehenden Widerrufsverzicht oder entfernt den Snapshot. group, group_id und cancellation_condition_group sind alternative Gruppenselektoren. /remove oder remove=true entfernt; ignore_withdrawal beziehungsweise withdrawal_ignore aktualisiert den Verzicht. Änderungen werden mit Historie und Notiz protokolliert; ohne neuen Zustand liefert update=false den unveränderten Snapshot.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true,"cancellation_conditions":false,"cancellation_conditions_history":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"cancellation_conditions":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Snapshot der ausgewählten Stornierungsbedingungen, abhängig von deren Regeln."},{"const":false}]},"cancellation_conditions_history":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"booking":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"group":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"group_id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"cancellation_condition_group":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"remove":{"x-edoobox-canonicalType":"boolean","description":"true, 1 sowie Text 1/true/yes/on werden als wahr erkannt; andere Werte als falsch."},"ignore_withdrawal":{"x-edoobox-canonicalType":"boolean","description":"Entsprechend dem nativen Boolean-Parser normalisiert; aktualisiert den Widerrufsverzicht."},"withdrawal_ignore":{"x-edoobox-canonicalType":"boolean","description":"Alias von ignore_withdrawal."},"reason":{"type":["string","number","boolean","null"]}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Stornierungsbedingungen einer Buchung ändern","value":{"data":{"group":"cancellationconditiongroup_012345abcdef_123456","reason":"Manuelle Vereinbarung"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":425}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/cancellationcondition Stornierungsbedingungen einer Buchung ändern Übernimmt eine cancellationconditiongroup als neuen Snapshot in die Buchung, aktualisiert einen bestehenden Widerrufsverzicht oder entfernt den Snapshot. group, group_id und cancellation_condition_group sind alternative Gruppenselektoren. /remove oder remove=true entfernt; ignore_withdrawal beziehungsweise withdrawal_ignore aktualisiert den Verzicht. Änderungen werden mit Historie und Notiz protokolliert; ohne neuen Zustand liefert update=false den unveränderten Snapshot. - Operation-ID: put_v2_booking_id_cancellationcondition - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-cancellationcondition-98654429b854/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "group": "cancellationconditiongroup_012345abcdef_123456", "reason": "Manuelle Vereinbarung" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/cancellationcondition' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/cancellationcondition"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "group": "cancellationconditiongroup_012345abcdef_123456", "reason": "Manuelle Vereinbarung" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/cancellationcondition" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"group\":\"cancellationconditiongroup_012345abcdef_123456\",\"reason\":\"Manuelle Vereinbarung\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_cancellationcondition","summary":"Stornierungsbedingungen einer Buchung ändern","description":"Übernimmt eine cancellationconditiongroup als neuen Snapshot in die Buchung, aktualisiert einen bestehenden Widerrufsverzicht oder entfernt den Snapshot. group, group_id und cancellation_condition_group sind alternative Gruppenselektoren. /remove oder remove=true entfernt; ignore_withdrawal beziehungsweise withdrawal_ignore aktualisiert den Verzicht. Änderungen werden mit Historie und Notiz protokolliert; ohne neuen Zustand liefert update=false den unveränderten Snapshot.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true,"cancellation_conditions":false,"cancellation_conditions_history":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"cancellation_conditions":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Snapshot der ausgewählten Stornierungsbedingungen, abhängig von deren Regeln."},{"const":false}]},"cancellation_conditions_history":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"booking":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"group":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"group_id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"cancellation_condition_group":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"remove":{"x-edoobox-canonicalType":"boolean","description":"true, 1 sowie Text 1/true/yes/on werden als wahr erkannt; andere Werte als falsch."},"ignore_withdrawal":{"x-edoobox-canonicalType":"boolean","description":"Entsprechend dem nativen Boolean-Parser normalisiert; aktualisiert den Widerrufsverzicht."},"withdrawal_ignore":{"x-edoobox-canonicalType":"boolean","description":"Alias von ignore_withdrawal."},"reason":{"type":["string","number","boolean","null"]}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Stornierungsbedingungen einer Buchung ändern","value":{"data":{"group":"cancellationconditiongroup_012345abcdef_123456","reason":"Manuelle Vereinbarung"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":424}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/cancellationcondition/remove Stornierungsbedingungen einer Buchung ändern Übernimmt eine cancellationconditiongroup als neuen Snapshot in die Buchung, aktualisiert einen bestehenden Widerrufsverzicht oder entfernt den Snapshot. group, group_id und cancellation_condition_group sind alternative Gruppenselektoren. /remove oder remove=true entfernt; ignore_withdrawal beziehungsweise withdrawal_ignore aktualisiert den Verzicht. Änderungen werden mit Historie und Notiz protokolliert; ohne neuen Zustand liefert update=false den unveränderten Snapshot. - Operation-ID: put_v2_booking_id_cancellationcondition_remove - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-cancellationcondition-remove-6ce13477317e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/cancellationcondition/remove' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/cancellationcondition/remove"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/cancellationcondition/remove" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_cancellationcondition_remove","summary":"Stornierungsbedingungen einer Buchung ändern","description":"Übernimmt eine cancellationconditiongroup als neuen Snapshot in die Buchung, aktualisiert einen bestehenden Widerrufsverzicht oder entfernt den Snapshot. group, group_id und cancellation_condition_group sind alternative Gruppenselektoren. /remove oder remove=true entfernt; ignore_withdrawal beziehungsweise withdrawal_ignore aktualisiert den Verzicht. Änderungen werden mit Historie und Notiz protokolliert; ohne neuen Zustand liefert update=false den unveränderten Snapshot.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true,"cancellation_conditions":false,"cancellation_conditions_history":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"cancellation_conditions":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Snapshot der ausgewählten Stornierungsbedingungen, abhängig von deren Regeln."},{"const":false}]},"cancellation_conditions_history":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"booking":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"group":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"group_id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"cancellation_condition_group":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["cancellationconditiongroup_012345abcdef_123456"]},"remove":{"x-edoobox-canonicalType":"boolean","description":"true, 1 sowie Text 1/true/yes/on werden als wahr erkannt; andere Werte als falsch."},"ignore_withdrawal":{"x-edoobox-canonicalType":"boolean","description":"Entsprechend dem nativen Boolean-Parser normalisiert; aktualisiert den Widerrufsverzicht."},"withdrawal_ignore":{"x-edoobox-canonicalType":"boolean","description":"Alias von ignore_withdrawal."},"reason":{"type":["string","number","boolean","null"]}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Stornierungsbedingungen einer Buchung ändern","value":{"data":{}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/certificate.pdf/{participantOrUserId} Teilnahmezertifikat einer Teilnehmerposition laden Rendert das Teilnahmezertifikat für participantOrUserId innerhalb der angegebenen Buchung. Eine participant-ID bezeichnet die konkrete Teilnahme; eine ältere user-ID ist nur bei genau einer passenden Teilnehmerposition zulässig. Sichtbarkeit und Präsenzvoraussetzungen bleiben kontoabhängig. Ein optionales cacheToken wird nicht ausgewertet; die Antwort enthält PDF-Bytes. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden. - Operation-ID: get_v2_booking_id_certificate_pdf_participantOrUserId - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-certificate-pdf-participantoruserid-92dba7eba8c1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | | | participantOrUserId | path | ja | Konkrete Teilnehmer-ID; ältere user-ID nur bei eindeutigem Teilnehmer innerhalb der Buchung. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque booking v2 ID returned by this account. Synthetic example is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"participantOrUserId","in":"path","required":true,"schema":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"description":"Konkrete Teilnehmer-ID; ältere user-ID nur bei eindeutigem Teilnehmer innerhalb der Buchung."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${PATH_PARTICIPANTORUSERID:?PATH_PARTICIPANTORUSERID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/certificate.pdf/'"${PATH_PARTICIPANTORUSERID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/certificate.pdf/{participantOrUserId}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); path = path.replaceAll("{participantOrUserId}", encodeURIComponent(required("PATH_PARTICIPANTORUSERID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/certificate.pdf/{participantOrUserId}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) path = path.replace("{participantOrUserId}", urllib.parse.quote(os.environ["PATH_PARTICIPANTORUSERID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_certificate_pdf_participantOrUserId","summary":"Teilnahmezertifikat einer Teilnehmerposition laden","description":"Rendert das Teilnahmezertifikat für participantOrUserId innerhalb der angegebenen Buchung. Eine participant-ID bezeichnet die konkrete Teilnahme; eine ältere user-ID ist nur bei genau einer passenden Teilnehmerposition zulässig. Sichtbarkeit und Präsenzvoraussetzungen bleiben kontoabhängig. Ein optionales cacheToken wird nicht ausgewertet; die Antwort enthält PDF-Bytes. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque booking v2 ID returned by this account. Synthetic example is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"participantOrUserId","in":"path","required":true,"schema":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"description":"Konkrete Teilnehmer-ID; ältere user-ID nur bei eindeutigem Teilnehmer innerhalb der Buchung."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Direkt gerendertes PDF; kontoabhängige Sichtbarkeit und Vorlagen bestimmen den Inhalt. Bei ungültigen Selektoren ist stattdessen eine JSON-Fehlerantwort möglich.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingfunc.php","method":"_getCertificatePDF","line":2648},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/certificate.pdf/{participantOrUserId}/{cacheToken} Teilnahmezertifikat einer Teilnehmerposition laden Rendert das Teilnahmezertifikat für participantOrUserId innerhalb der angegebenen Buchung. Eine participant-ID bezeichnet die konkrete Teilnahme; eine ältere user-ID ist nur bei genau einer passenden Teilnehmerposition zulässig. Sichtbarkeit und Präsenzvoraussetzungen bleiben kontoabhängig. Ein optionales cacheToken wird nicht ausgewertet; die Antwort enthält PDF-Bytes. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden. - Operation-ID: get_v2_booking_id_certificate_pdf_participantOrUserId_cacheToken - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-certificate-pdf-participantoruserid-cachetoken-af736f948b75/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | | | participantOrUserId | path | ja | Konkrete Teilnehmer-ID; ältere user-ID nur bei eindeutigem Teilnehmer innerhalb der Buchung. | | cacheToken | path | ja | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque booking v2 ID returned by this account. Synthetic example is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"participantOrUserId","in":"path","required":true,"schema":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"description":"Konkrete Teilnehmer-ID; ältere user-ID nur bei eindeutigem Teilnehmer innerhalb der Buchung."},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${PATH_PARTICIPANTORUSERID:?PATH_PARTICIPANTORUSERID setzen}" : "${PATH_CACHETOKEN:?PATH_CACHETOKEN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/certificate.pdf/'"${PATH_PARTICIPANTORUSERID}"'/'"${PATH_CACHETOKEN}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/certificate.pdf/{participantOrUserId}/{cacheToken}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); path = path.replaceAll("{participantOrUserId}", encodeURIComponent(required("PATH_PARTICIPANTORUSERID"))); path = path.replaceAll("{cacheToken}", encodeURIComponent(required("PATH_CACHETOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/certificate.pdf/{participantOrUserId}/{cacheToken}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) path = path.replace("{participantOrUserId}", urllib.parse.quote(os.environ["PATH_PARTICIPANTORUSERID"], safe="")) path = path.replace("{cacheToken}", urllib.parse.quote(os.environ["PATH_CACHETOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_certificate_pdf_participantOrUserId_cacheToken","summary":"Teilnahmezertifikat einer Teilnehmerposition laden","description":"Rendert das Teilnahmezertifikat für participantOrUserId innerhalb der angegebenen Buchung. Eine participant-ID bezeichnet die konkrete Teilnahme; eine ältere user-ID ist nur bei genau einer passenden Teilnehmerposition zulässig. Sichtbarkeit und Präsenzvoraussetzungen bleiben kontoabhängig. Ein optionales cacheToken wird nicht ausgewertet; die Antwort enthält PDF-Bytes. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque booking v2 ID returned by this account. Synthetic example is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"participantOrUserId","in":"path","required":true,"schema":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"description":"Konkrete Teilnehmer-ID; ältere user-ID nur bei eindeutigem Teilnehmer innerhalb der Buchung."},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}],"responses":{"200":{"description":"Direkt gerendertes PDF; kontoabhängige Sichtbarkeit und Vorlagen bestimmen den Inhalt. Bei ungültigen Selektoren ist stattdessen eine JSON-Fehlerantwort möglich.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Bookingfunc.php","method":"_getCertificatePDF","line":2648,"reason":"The certificate function consumes participant/user in segment5; the exact trailing Vue cAe timestamp remains a cache token."},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/9","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":918,"lineEnd":921,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"Certificate PDF branch dispatches to _getCertificatePDF."},"reason":"Validates an existing booking participant and renders the attendance certificate PDF without sending mail or persisting issuance.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/9","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingfunc.php","method":"_getCertificatePDF","line":2648},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/dashboard Daten für das Buchungsdashboard laden Lädt die Buchung, ihren erweiterten Buchungsstand sowie zugehörige Kunden, Angebote, Termine, Preiskategorien, Fragen, Leistungen, Salden, Rechnungen und Mitgliedschaftsvorteile. Die Pfad-ID muss eine Buchung im ausgewählten Konto bezeichnen. - Operation-ID: get_v2_booking_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-dashboard-5da3d9bb0aeb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_dashboard","summary":"Daten für das Buchungsdashboard laden","description":"Lädt die Buchung, ihren erweiterten Buchungsstand sowie zugehörige Kunden, Angebote, Termine, Preiskategorien, Fragen, Leistungen, Salden, Rechnungen und Mitgliedschaftsvorteile. Die Pfad-ID muss eine Buchung im ausgewählten Konto bezeichnen.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Erweiterte fachliche Rückgabe in data; die aufgeführten Sammlungen können leer sein und enthalten ausschliesslich öffentliche Referenzen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"config":{"deliverables_exist":false,"questiongroups_exist":false},"booking_data":{"transactions":[{"id":"transaction_012345abcdef_123456","number":"2026-1001","amount":120,"currency":"CHF"}],"users":[{"active":true,"participant":"participant_012345abcdef_123456","position":"person","user":"user_012345abcdef_123456","pricecategory":"pricecategory_012345abcdef_123456","cert_visible":false}],"payments":[],"promotion":false,"multi_offer":false,"dates":[],"lessons_booking":false,"invoice_address":{"active":false,"b2b":false,"data0":"","data1":"","data2":"","data3":"","data4":"","data5":"","data7":"","data8":""},"label":false,"ip_address":"192.0.2.1","offer":"offer_012345abcdef_123456","questiongroup":[],"multi_offer_questiongroup":[],"deliverables":[],"pricecategory":{"pricecategory_012345abcdef_123456":{"id":"pricecategory_012345abcdef_123456","count":1,"name":"Standard","amount":120,"default":true,"offer":"offer_012345abcdef_123456"}},"legaltexts":[],"cancellation_conditions":false,"cancellation_conditions_history":[],"cancellation_fee_preview":false,"fees":[]},"booking":{"id":"booking_012345abcdef_123456","offer":"offer_012345abcdef_123456","owner":"user_012345abcdef_123456","waiting_list":false,"canceled":false},"dates":[],"pricecategorys":[],"users":[],"balances":[{"total":0,"currency_iso":"CHF"}],"invoices":[],"offers":[],"questiongroups":[],"questions":[],"deliverables":[],"booking_id":"booking_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"config":{"type":"object","properties":{"deliverables_exist":{"type":"boolean"},"questiongroups_exist":{"type":"boolean"}},"additionalProperties":true},"booking_data":{"type":"object","properties":{"transactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"]},"number":{"type":["number","string"]},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true}},"users":{"type":"array","items":{"type":"object","properties":{"active":{"type":"boolean"},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"const":false}]},"position":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"cert_visible":{"type":"boolean"},"unnamed":{"type":"boolean"},"multi_offer":{"type":"array","items":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]}},"additionalProperties":true}}},"additionalProperties":true}},"payments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","examples":["paytrans_012345abcdef_123456"]},"refundable":{"type":"boolean"},"payment":{"type":"object","properties":{"type":{"type":"string"},"token":{"type":"string"},"currency":{"type":"string"},"amount":{"type":"number"},"refound":{"anyOf":[{"type":"object","properties":{"repaid_amount":{"type":"number"},"refundable_amount":{"type":"number"}},"additionalProperties":true},{"const":false}]},"status":{"type":"integer"},"date":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},"additionalProperties":true}},"additionalProperties":true}},"promotion":{"anyOf":[{"type":"object","properties":{"code":{"type":"string"},"currency":{"type":"string"},"active":{"type":"boolean"},"value":{"type":["number","string"]},"id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"]},"minimum_booking_amount":{"type":["number","string"]}},"additionalProperties":true},{"const":false}]},"multi_offer":{"type":"boolean"},"dates":{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"lessons_booking":{"type":"boolean"},"invoice_address":{"type":"object","properties":{"data0":{"type":"string"},"data1":{"type":"string"},"data2":{"type":"string"},"data3":{"type":"string"},"data4":{"type":"string"},"data5":{"type":"string"},"data6":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","examples":["country_012345abcdef_123456"]},{"const":false}]},"data7":{"type":"string"},"data8":{"type":"string"},"active":{"type":"boolean"},"b2b":{"type":"boolean"}},"additionalProperties":true},"label":{"anyOf":[{"type":"string"},{"const":false}]},"ip_address":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"questiongroup":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"multi_offer_questiongroup":{"type":["object","array"],"additionalProperties":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"items":{}},"deliverables":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"detail":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"number"}},"additionalProperties":true},"count":{"type":["number","string"]},"amount":{"type":["number","string"]},"currency":{"type":"string"},"vat":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"]},"vat_percent":{"type":["number","string"]},"vat_number":{"type":"string"}},"additionalProperties":true},{"const":false}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"detail":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"number"}},"additionalProperties":true},"count":{"type":["number","string"]},"amount":{"type":["number","string"]},"currency":{"type":"string"},"vat":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"]},"vat_percent":{"type":["number","string"]},"vat_number":{"type":"string"}},"additionalProperties":true},{"const":false}]}},"additionalProperties":true}}]},"pricecategory":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"count":{"type":"integer"},"name":{"type":"string"},"amount":{"type":"number"},"default":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"count":{"type":"integer"},"name":{"type":"string"},"amount":{"type":"number"},"default":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"legaltexts":{"type":"array","items":{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"]},"legalversion":{"anyOf":[{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","examples":["legalversion_012345abcdef_123456"]},{"const":false}]},"accepted":{"type":"boolean"},"require":{"type":"boolean"}},"additionalProperties":true}},"cancellation_conditions":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Snapshot mit kontoabhängigen Stornierungs- und Widerrufsregeln."},{"const":false}]},"cancellation_conditions_history":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Historieneinträge mit Akteur und den damaligen Snapshotwerten."}},"cancellation_fee_preview":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Aktuelle Gebührenvorschau gemäss Stornierungsbedingungen und Zahlungsstand."},{"const":false}]},"fees":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Öffentliche Gebührenobjekte dieser Buchung."}},"membership_benefit":{"type":"object","properties":{"active":{"type":"boolean"},"suppressed":{"type":"boolean"},"removed":{"type":"boolean"},"application_count":{"type":"integer"},"discounts":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"amount":{"type":"string"}},"additionalProperties":true}},"participants":{"type":"array","items":{"type":"object","properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["usermembership_012345abcdef_123456"]},"membership_name":{"type":"string"},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["membershipplan_012345abcdef_123456"]},"discount_amount":{"type":"string"},"currency":{"type":"string"},"restores_pass_use":{"type":"boolean"}},"additionalProperties":true}},"restores_pass_use":{"type":"boolean"},"removal_reason":{"type":"string"},"removed_at":{"type":["integer","null"]}},"additionalProperties":true},"membership_passes":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"passes":{"type":"array","items":{"type":"object","properties":{"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","examples":["usermembership_012345abcdef_123456"]},"name":{"type":"string"},"access":{"type":"boolean"},"discount":{"type":"boolean"}},"additionalProperties":true}}},"additionalProperties":true}}},"additionalProperties":true},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Felder des Booking-Structures, insbesondere Inhaber, Angebot, Status und Teilnehmer."},"dates":{"anyOf":[{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}},{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}}]},"pricecategorys":{"anyOf":[{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}},{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}}]},"users":{"anyOf":[{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}},{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}}]},"balances":{"type":"array","items":{"type":"object","properties":{"total":{"type":"number"},"currency_iso":{"type":"string"}},"additionalProperties":true}},"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"status":{"type":"integer"},"number":{"type":"string"},"date_create":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"date_payuntil":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"refresh_suggested":{"type":"boolean"}},"additionalProperties":true}},"offers":{"anyOf":[{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}},{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}}]},"questiongroups":{"anyOf":[{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}},{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}}]},"questions":{"anyOf":[{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}},{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}}]},"deliverables":{"anyOf":[{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}},{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}}]},"booking_id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/10","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":939,"lineEnd":941,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"Dashboard action selects Bookingdashboard."},"reason":"Aggregates current booking, balances, invoices, membership summaries and nested read resources for the dashboard.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/10","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":939}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/data Vollständige Buchungsübersicht lesen Liest den gespeicherten Buchungsstand mit Teilnehmerpositionen, Preisen, Zusatzleistungen, Antworten, Zahlungen, Rechnungsadresse, Rechts- und Stornierungsinformationen. Genau eine bestehende Buchung wird über die Pfad-ID oder den ersten filter-Eintrag mit property=id ausgewählt; dies ist keine Listenabfrage. - Operation-ID: get_v2_booking_id_data - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-data-5aa3c5786d34/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/data' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/data"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/data" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_data","summary":"Vollständige Buchungsübersicht lesen","description":"Liest den gespeicherten Buchungsstand mit Teilnehmerpositionen, Preisen, Zusatzleistungen, Antworten, Zahlungen, Rechnungsadresse, Rechts- und Stornierungsinformationen. Genau eine bestehende Buchung wird über die Pfad-ID oder den ersten filter-Eintrag mit property=id ausgewählt; dies ist keine Listenabfrage.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"Erweiterte fachliche Rückgabe in data; die aufgeführten Sammlungen können leer sein und enthalten ausschliesslich öffentliche Referenzen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"transactions":[{"id":"transaction_012345abcdef_123456","number":"2026-1001","amount":120,"currency":"CHF"}],"users":[{"active":true,"participant":"participant_012345abcdef_123456","position":"person","user":"user_012345abcdef_123456","pricecategory":"pricecategory_012345abcdef_123456","cert_visible":false}],"payments":[],"promotion":false,"multi_offer":false,"dates":[],"lessons_booking":false,"invoice_address":{"active":false,"b2b":false,"data0":"","data1":"","data2":"","data3":"","data4":"","data5":"","data7":"","data8":""},"label":false,"ip_address":"192.0.2.1","offer":"offer_012345abcdef_123456","questiongroup":[],"multi_offer_questiongroup":[],"deliverables":[],"pricecategory":{"pricecategory_012345abcdef_123456":{"id":"pricecategory_012345abcdef_123456","count":1,"name":"Standard","amount":120,"default":true,"offer":"offer_012345abcdef_123456"}},"legaltexts":[],"cancellation_conditions":false,"cancellation_conditions_history":[],"cancellation_fee_preview":false,"fees":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"transactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"]},"number":{"type":["number","string"]},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true}},"users":{"type":"array","items":{"type":"object","properties":{"active":{"type":"boolean"},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"const":false}]},"position":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"cert_visible":{"type":"boolean"},"unnamed":{"type":"boolean"},"multi_offer":{"type":"array","items":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]}},"additionalProperties":true}}},"additionalProperties":true}},"payments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","examples":["paytrans_012345abcdef_123456"]},"refundable":{"type":"boolean"},"payment":{"type":"object","properties":{"type":{"type":"string"},"token":{"type":"string"},"currency":{"type":"string"},"amount":{"type":"number"},"refound":{"anyOf":[{"type":"object","properties":{"repaid_amount":{"type":"number"},"refundable_amount":{"type":"number"}},"additionalProperties":true},{"const":false}]},"status":{"type":"integer"},"date":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},"additionalProperties":true}},"additionalProperties":true}},"promotion":{"anyOf":[{"type":"object","properties":{"code":{"type":"string"},"currency":{"type":"string"},"active":{"type":"boolean"},"value":{"type":["number","string"]},"id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"]},"minimum_booking_amount":{"type":["number","string"]}},"additionalProperties":true},{"const":false}]},"multi_offer":{"type":"boolean"},"dates":{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"lessons_booking":{"type":"boolean"},"invoice_address":{"type":"object","properties":{"data0":{"type":"string"},"data1":{"type":"string"},"data2":{"type":"string"},"data3":{"type":"string"},"data4":{"type":"string"},"data5":{"type":"string"},"data6":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","examples":["country_012345abcdef_123456"]},{"const":false}]},"data7":{"type":"string"},"data8":{"type":"string"},"active":{"type":"boolean"},"b2b":{"type":"boolean"}},"additionalProperties":true},"label":{"anyOf":[{"type":"string"},{"const":false}]},"ip_address":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"questiongroup":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"multi_offer_questiongroup":{"type":["object","array"],"additionalProperties":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"items":{}},"deliverables":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"detail":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"number"}},"additionalProperties":true},"count":{"type":["number","string"]},"amount":{"type":["number","string"]},"currency":{"type":"string"},"vat":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"]},"vat_percent":{"type":["number","string"]},"vat_number":{"type":"string"}},"additionalProperties":true},{"const":false}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"detail":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"number"}},"additionalProperties":true},"count":{"type":["number","string"]},"amount":{"type":["number","string"]},"currency":{"type":"string"},"vat":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"]},"vat_percent":{"type":["number","string"]},"vat_number":{"type":"string"}},"additionalProperties":true},{"const":false}]}},"additionalProperties":true}}]},"pricecategory":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"count":{"type":"integer"},"name":{"type":"string"},"amount":{"type":"number"},"default":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"count":{"type":"integer"},"name":{"type":"string"},"amount":{"type":"number"},"default":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"legaltexts":{"type":"array","items":{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"]},"legalversion":{"anyOf":[{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","examples":["legalversion_012345abcdef_123456"]},{"const":false}]},"accepted":{"type":"boolean"},"require":{"type":"boolean"}},"additionalProperties":true}},"cancellation_conditions":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Snapshot mit kontoabhängigen Stornierungs- und Widerrufsregeln."},{"const":false}]},"cancellation_conditions_history":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Historieneinträge mit Akteur und den damaligen Snapshotwerten."}},"cancellation_fee_preview":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Aktuelle Gebührenvorschau gemäss Stornierungsbedingungen und Zahlungsstand."},{"const":false}]},"fees":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Öffentliche Gebührenobjekte dieser Buchung."}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Booking.php","line":787,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/11","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":787,"lineEnd":789,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"The exact data action invokes getBookingData."},"reason":"Reconstructs stored booking data, participants, cancellations and fee previews without changing booking state.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/11","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":787}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/dates Gebuchte Lektionstermine vollständig ersetzen Ersetzt bei einer Lektionenbuchung die gesamte Terminwahl durch data.dates. Eine echte JSON-Liste ist erforderlich; [] entfernt alle Termine. Alle IDs müssen Termine des gebuchten Angebots sein und werden vor dem ersten Entfernen geprüft. Teilnehmerzuordnungen und Preise werden nachgeführt. - Operation-ID: put_v2_booking_id_dates - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-dates-c28c9f45f0c6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "dates": [ "date_012345abcdef_123456" ] } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/dates' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/dates"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "dates": [ "date_012345abcdef_123456" ] } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/dates" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"dates\":[\"date_012345abcdef_123456\"]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_dates","summary":"Gebuchte Lektionstermine vollständig ersetzen","description":"Ersetzt bei einer Lektionenbuchung die gesamte Terminwahl durch data.dates. Eine echte JSON-Liste ist erforderlich; [] entfernt alle Termine. Alle IDs müssen Termine des gebuchten Angebots sein und werden vor dem ersten Entfernen geprüft. Teilnehmerzuordnungen und Preise werden nachgeführt.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand inklusive eigenem data/error/type-Umschlag."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"dates":{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["date_012345abcdef_123456"]}}},"additionalProperties":true,"required":["dates"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Gebuchte Lektionstermine vollständig ersetzen","value":{"data":{"dates":["date_012345abcdef_123456"]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":450}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/deliverables Menge einer gebuchten Zusatzleistung ändern Setzt die Menge count für deliverables und berechnet den Buchungspreis neu. count wird mit int normalisiert und muss danach zwischen 0 und 9998 liegen; 0 entfernt die gewählte Menge. Die Leistung wird im Kontext der bestehenden Buchung verarbeitet. - Operation-ID: put_v2_booking_id_deliverables - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-deliverables-e3171f217026/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "deliverables": "deliverables_012345abcdef_123456", "count": 2 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/deliverables' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/deliverables"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "deliverables": "deliverables_012345abcdef_123456", "count": 2 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/deliverables" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"deliverables\":\"deliverables_012345abcdef_123456\",\"count\":2}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_deliverables","summary":"Menge einer gebuchten Zusatzleistung ändern","description":"Setzt die Menge count für deliverables und berechnet den Buchungspreis neu. count wird mit int normalisiert und muss danach zwischen 0 und 9998 liegen; 0 entfernt die gewählte Menge. Die Leistung wird im Kontext der bestehenden Buchung verarbeitet.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand inklusive eigenem data/error/type-Umschlag."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deliverables":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["deliverables_012345abcdef_123456"]},"count":{"x-edoobox-canonicalType":"integer","description":"Mit int normalisiert; danach 0 bis 9998. 0 setzt die Menge auf null."}},"additionalProperties":true,"required":["deliverables","count"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Menge einer gebuchten Zusatzleistung ändern","value":{"data":{"deliverables":"deliverables_012345abcdef_123456","count":2}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":468}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/invoice_address Abweichende Rechnungsadresse ändern Aktiviert oder deaktiviert die abweichende Rechnungsadresse über active beziehungsweise invoice_address und ändert über data0 bis data8 deren Adressfelder. data6 enthält eine öffentliche country-ID; b2b wird anhand der Umsatzsteuer- und Adressvoraussetzungen geprüft. Eine Preis-/Steuerneuberechnung kann folgen; die Rückgabe ergänzt den Buchungsstand. - Operation-ID: put_v2_booking_id_invoice_address - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-invoice-address-3327863cc56d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "active": true, "data1": "Beispiel AG", "data2": "Alex Beispiel", "data3": "Musterstrasse 1", "data4": "8000 Zürich", "data5": "rechnung@example.invalid", "data6": "country_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/invoice_address' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/invoice_address"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "active": true, "data1": "Beispiel AG", "data2": "Alex Beispiel", "data3": "Musterstrasse 1", "data4": "8000 Zürich", "data5": "rechnung@example.invalid", "data6": "country_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/invoice_address" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"active\":true,\"data1\":\"Beispiel AG\",\"data2\":\"Alex Beispiel\",\"data3\":\"Musterstrasse 1\",\"data4\":\"8000 Zürich\",\"data5\":\"rechnung@example.invalid\",\"data6\":\"country_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_invoice_address","summary":"Abweichende Rechnungsadresse ändern","description":"Aktiviert oder deaktiviert die abweichende Rechnungsadresse über active beziehungsweise invoice_address und ändert über data0 bis data8 deren Adressfelder. data6 enthält eine öffentliche country-ID; b2b wird anhand der Umsatzsteuer- und Adressvoraussetzungen geprüft. Eine Preis-/Steuerneuberechnung kann folgen; die Rückgabe ergänzt den Buchungsstand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"data0":{"type":["string","number","boolean","null"]},"data1":{"type":["string","number","boolean","null"]},"data2":{"type":["string","number","boolean","null"]},"data3":{"type":["string","number","boolean","null"]},"data4":{"type":["string","number","boolean","null"]},"data5":{"type":["string","number","boolean","null"]},"data6":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["country_012345abcdef_123456"]},{"enum":[false,null,""]}]},"data7":{"type":["string","number","boolean","null"]},"data8":{"type":["string","number","boolean","null"]},"active":{"x-edoobox-canonicalType":"boolean","description":"Die native PHP-Wahrheitsauswertung normalisiert den Wert; true/false sind die kanonische JSON-Eingabe."},"invoice_address":{"x-edoobox-canonicalType":"boolean","description":"Die native PHP-Wahrheitsauswertung normalisiert den Wert; true/false sind die kanonische JSON-Eingabe."},"b2b":{"x-edoobox-canonicalType":"boolean","description":"Die native PHP-Wahrheitsauswertung normalisiert den Wert; true/false sind die kanonische JSON-Eingabe."}},"additionalProperties":true,"anyOf":[{"required":["active"]},{"required":["invoice_address"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Abweichende Rechnungsadresse ändern","value":{"data":{"active":true,"data1":"Beispiel AG","data2":"Alex Beispiel","data3":"Musterstrasse 1","data4":"8000 Zürich","data5":"rechnung@example.invalid","data6":"country_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":506}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/invoice_address_suggestions Frühere Rechnungsadressen vorschlagen Ermittelt höchstens vier unterschiedliche aktive Rechnungsadressen aus früheren Buchungen des Buchungsinhabers. Die aktuelle Buchung wird ausgeschlossen. Jeder Vorschlag enthält data0 bis data8, active und b2b, einen abgeleiteten 16-stelligen id-Fingerprint sowie bookings mit öffentlichen Buchungs-IDs und Buchungsnummern der zugehörigen früheren Buchungen. - Operation-ID: get_v2_booking_id_invoice_address_suggestions - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-invoice-address-suggestions-014c1eb79efc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/invoice_address_suggestions' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/invoice_address_suggestions"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/invoice_address_suggestions" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_invoice_address_suggestions","summary":"Frühere Rechnungsadressen vorschlagen","description":"Ermittelt höchstens vier unterschiedliche aktive Rechnungsadressen aus früheren Buchungen des Buchungsinhabers. Die aktuelle Buchung wird ausgeschlossen. Jeder Vorschlag enthält data0 bis data8, active und b2b, einen abgeleiteten 16-stelligen id-Fingerprint sowie bookings mit öffentlichen Buchungs-IDs und Buchungsnummern der zugehörigen früheren Buchungen.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Erweiterte fachliche Rückgabe in data; die aufgeführten Sammlungen können leer sein und enthalten ausschliesslich öffentliche Referenzen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"suggestions":[{"active":true,"b2b":false,"data0":"","data1":"Beispiel AG","data2":"Alex Beispiel","data3":"Musterstrasse 1","data4":"8000 Zürich","data5":"rechnung@example.invalid","data6":"country_012345abcdef_123456","data7":"","data8":"","id":"012345abcdef6789","bookings":[{"id":"booking_012345abcdef_123457","number":"2026-0999"}]}]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"suggestions":{"type":"array","items":{"type":"object","properties":{"data0":{"type":"string"},"data1":{"type":"string"},"data2":{"type":"string"},"data3":{"type":"string"},"data4":{"type":"string"},"data5":{"type":"string"},"data6":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","examples":["country_012345abcdef_123456"]},{"const":false}]},"data7":{"type":"string"},"data8":{"type":"string"},"active":{"type":"boolean"},"b2b":{"type":"boolean"},"id":{"type":"string","pattern":"^[a-f0-9]{16}$","description":"Abgeleiteter Adress-Fingerprint, keine Datenbank-ID."},"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"number":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true},"maxItems":4}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/12","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":790,"lineEnd":865,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"Decodes current booking, queries account-scoped account/booking owner and delegates to invoice address suggestions."},"reason":"Suggests invoice addresses already recorded for the booking owner/account.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/12","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":790}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/membership_benefit Angewendeten Mitgliedschaftsrabatt entfernen Entfernt den angewendeten Mitgliedschaftsvorteil dauerhaft für diese Buchung und berechnet Preise und Kontobewegungen neu. reason und request_id müssen nach trim nicht leer sein. Dieselbe request_id unterstützt Wiederholungen im nativen Ablauf; die Rückgabe enthält entfernte Anwendungen, zurückgeführte Nutzungen und den aktuellen Vorteilstatus. - Operation-ID: put_v2_booking_id_membership_benefit - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-membership-benefit-813ec71205e3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "reason": "Kulanzkorrektur", "request_id": "benefit-removal-demo-001" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/membership_benefit' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/membership_benefit"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "reason": "Kulanzkorrektur", "request_id": "benefit-removal-demo-001" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/membership_benefit" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"reason\":\"Kulanzkorrektur\",\"request_id\":\"benefit-removal-demo-001\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_membership_benefit","summary":"Angewendeten Mitgliedschaftsrabatt entfernen","description":"Entfernt den angewendeten Mitgliedschaftsvorteil dauerhaft für diese Buchung und berechnet Preise und Kontobewegungen neu. reason und request_id müssen nach trim nicht leer sein. Dieselbe request_id unterstützt Wiederholungen im nativen Ablauf; die Rückgabe enthält entfernte Anwendungen, zurückgeführte Nutzungen und den aktuellen Vorteilstatus.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true,"removed":1,"reversed_uses":0,"membership_benefit":{"active":false,"suppressed":true,"removed":true,"application_count":1,"discounts":[{"currency":"CHF","amount":"10.00"}],"participants":[],"restores_pass_use":false,"removal_reason":"Kulanzkorrektur","removed_at":1792400000}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"removed":{"type":"integer"},"reversed_uses":{"type":"integer"},"membership_benefit":{"type":"object","properties":{"active":{"type":"boolean"},"suppressed":{"type":"boolean"},"removed":{"type":"boolean"},"application_count":{"type":"integer"},"discounts":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"amount":{"type":"string"}},"additionalProperties":true}},"participants":{"type":"array","items":{"type":"object","properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["usermembership_012345abcdef_123456"]},"membership_name":{"type":"string"},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["membershipplan_012345abcdef_123456"]},"discount_amount":{"type":"string"},"currency":{"type":"string"},"restores_pass_use":{"type":"boolean"}},"additionalProperties":true}},"restores_pass_use":{"type":"boolean"},"removal_reason":{"type":"string"},"removed_at":{"type":["integer","null"]}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"reason":{"type":["string","number","boolean"],"description":"Nach string-Cast und trim nicht leer."},"request_id":{"type":["string","number","boolean"],"description":"Nicht leerer Wiederholungsschlüssel nach string-Cast und trim; für denselben Auftrag wiederverwenden."}},"additionalProperties":true,"required":["reason","request_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Angewendeten Mitgliedschaftsrabatt entfernen","value":{"data":{"reason":"Kulanzkorrektur","request_id":"benefit-removal-demo-001"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":495}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/multi_offer Unterangebot einer Teilnehmerposition ändern Ordnet einer Teilnehmerposition das Unterangebot offer mit pricecategory zu oder entfernt diese Zuordnung mit pricecategory=false. participant ist massgeblich; ein eindeutiger user oder eine gespeicherte position dienen als Alternativen. Mehrdeutige Teilnehmer und ungültige Buchungs-/Angebotsbeziehungen werden abgelehnt; Preise werden neu berechnet. - Operation-ID: put_v2_booking_id_multi_offer - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-multi-offer-23d7abac36c1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "participant": "participant_012345abcdef_123456", "pricecategory": "pricecategory_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/multi_offer' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/multi_offer"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "participant": "participant_012345abcdef_123456", "pricecategory": "pricecategory_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/multi_offer" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"participant\":\"participant_012345abcdef_123456\",\"pricecategory\":\"pricecategory_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_multi_offer","summary":"Unterangebot einer Teilnehmerposition ändern","description":"Ordnet einer Teilnehmerposition das Unterangebot offer mit pricecategory zu oder entfernt diese Zuordnung mit pricecategory=false. participant ist massgeblich; ein eindeutiger user oder eine gespeicherte position dienen als Alternativen. Mehrdeutige Teilnehmer und ungültige Buchungs-/Angebotsbeziehungen werden abgelehnt; Preise werden neu berechnet.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand inklusive eigenem data/error/type-Umschlag."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"pricecategory":{"anyOf":[{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["pricecategory_012345abcdef_123456"]},{"const":false}]},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},{"enum":[false,null,""]}]},"position":{"type":"string","minLength":1}},"additionalProperties":true,"required":["offer","pricecategory"],"anyOf":[{"required":["participant"],"properties":{"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["participant_012345abcdef_123456"]}}},{"required":["user"],"properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]}}},{"required":["position"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Unterangebot einer Teilnehmerposition ändern","value":{"data":{"offer":"offer_012345abcdef_123456","participant":"participant_012345abcdef_123456","pricecategory":"pricecategory_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":477}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/pricecategory Preiskategorie einer Teilnehmerposition wechseln Ändert die Preiskategorie genau einer Teilnehmerposition innerhalb der Buchung und liefert den aktualisierten Buchungsstand. participant ist der eindeutige Selektor; user wird nur bei eindeutiger Zuordnung akzeptiert, position unterstützt gespeicherte Buchungspositionen. Die bestehenden geprüften Selektorvarianten bleiben massgeblich. - Operation-ID: put_v2_booking_id_pricecategory - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-pricecategory-a45d3cd46903/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "pricecategory": "pricecategory_012345abcdef_123456", "participant": "participant_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/pricecategory' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/pricecategory"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "pricecategory": "pricecategory_012345abcdef_123456", "participant": "participant_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/pricecategory" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"pricecategory\":\"pricecategory_012345abcdef_123456\",\"participant\":\"participant_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_pricecategory","summary":"Preiskategorie einer Teilnehmerposition wechseln","description":"Ändert die Preiskategorie genau einer Teilnehmerposition innerhalb der Buchung und liefert den aktualisierten Buchungsstand. participant ist der eindeutige Selektor; user wird nur bei eindeutiger Zuordnung akzeptiert, position unterstützt gespeicherte Buchungspositionen. Die bestehenden geprüften Selektorvarianten bleiben massgeblich.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"cancel":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand im eigenen data/error/type-Umschlag."},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["participant_012345abcdef_123456"]},{"const":false}]},"position":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque participant v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque user v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["user_012345abcdef_123456"]},{"enum":[false,null,""]}]},"position":{"anyOf":[{"type":"string"},{"const":false},{"type":"null"}]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque pricecategory v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["pricecategory_012345abcdef_123456"]}},"additionalProperties":true,"anyOf":[{"required":["participant"],"properties":{"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque participant v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["participant_012345abcdef_123456"]}}},{"required":["user"],"properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque user v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["user_012345abcdef_123456"]}}},{"required":["position"],"properties":{"position":{"type":"string","minLength":1}}}],"required":["pricecategory"]}},"additionalProperties":true},"examples":{"participant":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"pricecategory":"pricecategory_012345abcdef_123456","participant":"participant_012345abcdef_123456"}}},"user":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"pricecategory":"pricecategory_012345abcdef_123456","user":"user_012345abcdef_123456"}}}}}}},"x-edoobox-action-contract":{"review":"booking-pricecategory","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Booking.php","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","methods":["updateBooking","resolveBookingParticipantTarget","getSerializedBookingPositionSlot"]}],"preconditions":["Participant/user/position must resolve inside this account-owned booking. Multiple matching participant/serialized slots require an explicit selection.","A serialized position is usable only under the action-specific inactive-slot rules in resolveBookingParticipantTarget."],"errors":[{"error":"ED80100","error_code":"participant_not_found"},{"error":"ED80100","error_code":"participant_ambiguous"},{"error":"ED80100","error_code":"booking_not_found"}],"unresolved":["Current slot state, user/participant disambiguation and Offerte effects remain native; position format is intentionally open."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":234}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/promotion Promotionscode einer Buchung setzen oder entfernen Wendet den öffentlichen promotioncode auf die Buchung an und berechnet den Preis neu. promotioncode=false entfernt einen bereits vorhandenen Code; ohne vorhandenen Code ist dieser Entfernungspfad kein erfolgreicher Schreibvorgang. Die Rückgabe meldet update und den aktuellen Buchungsstand. - Operation-ID: put_v2_booking_id_promotion - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-promotion-1392eecf4efd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "promotioncode": "promotioncode_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/promotion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/promotion"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "promotioncode": "promotioncode_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/promotion" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"promotioncode\":\"promotioncode_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_promotion","summary":"Promotionscode einer Buchung setzen oder entfernen","description":"Wendet den öffentlichen promotioncode auf die Buchung an und berechnet den Preis neu. promotioncode=false entfernt einen bereits vorhandenen Code; ohne vorhandenen Code ist dieser Entfernungspfad kein erfolgreicher Schreibvorgang. Die Rückgabe meldet update und den aktuellen Buchungsstand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand inklusive eigenem data/error/type-Umschlag."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"promotioncode":{"anyOf":[{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["promotioncode_012345abcdef_123456"]},{"const":false}]}},"additionalProperties":true,"required":["promotioncode"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Promotionscode einer Buchung setzen oder entfernen","value":{"data":{"promotioncode":"promotioncode_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":486}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/question Antwort einer Buchung bearbeiten Speichert answer für question. Das Feld answer muss vorhanden sein; auch 0 und leerer Text sind Eingaben und keine fehlenden Werte. Bei Auswahlfragen muss answer eine erlaubte question-ID sein. Mit offer wird eine tatsächlich gebuchte Unterangebotsfrage adressiert; deren Fragekontext und erlaubte Antworten werden geprüft. - Operation-ID: put_v2_booking_id_question - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-question-7fad627db28f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "question": "question_012345abcdef_123456", "answer": "Vegetarisch" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/question' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/question"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "question": "question_012345abcdef_123456", "answer": "Vegetarisch" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/question" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"question\":\"question_012345abcdef_123456\",\"answer\":\"Vegetarisch\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_question","summary":"Antwort einer Buchung bearbeiten","description":"Speichert answer für question. Das Feld answer muss vorhanden sein; auch 0 und leerer Text sind Eingaben und keine fehlenden Werte. Bei Auswahlfragen muss answer eine erlaubte question-ID sein. Mit offer wird eine tatsächlich gebuchte Unterangebotsfrage adressiert; deren Fragekontext und erlaubte Antworten werden geprüft.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand inklusive eigenem data/error/type-Umschlag."}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"question":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["question_012345abcdef_123456"]},"answer":{"description":"Muss vorhanden sein. Freitext-/Zahlantwort bei Textfragen, erlaubte öffentliche question-ID bei Auswahlfragen. Auch 0, false und leerer Text werden als vorhandene Eingabe an die Frageverarbeitung übergeben."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["question","answer"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Antwort einer Buchung bearbeiten","value":{"data":{"question":"question_012345abcdef_123456","answer":"Vegetarisch"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":459}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/rebooking Buchung in ein anderes Angebot umbuchen oder kopieren Bucht die bestehende Buchung in offer um oder erstellt mit copy eine Kopie. pricecategory und pricecategory_map können Zielpreise festlegen. Abrechnungssperren, manuell zu prüfende Rechnungen und Teilnehmervalidierung können die Aktion blockieren; die bereits geprüften Aktionsschemas gelten unverändert. - Operation-ID: put_v2_booking_id_rebooking - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-rebooking-5dcd983f5f95/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/rebooking' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/rebooking"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/rebooking" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_rebooking","summary":"Buchung in ein anderes Angebot umbuchen oder kopieren","description":"Bucht die bestehende Buchung in offer um oder erstellt mit copy eine Kopie. pricecategory und pricecategory_map können Zielpreise festlegen. Abrechnungssperren, manuell zu prüfende Rechnungen und Teilnehmervalidierung können die Aktion blockieren; die bereits geprüften Aktionsschemas gelten unverändert.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"cancel":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand im eigenen data/error/type-Umschlag."},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["participant_012345abcdef_123456"]},{"const":false}]},"position":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque offer v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["offer_012345abcdef_123456"]},"copy":{"x-edoobox-canonicalType":"boolean","description":"PHP loose boolean comparison selects copy; absent copy uses the native rebooking behavior."},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque pricecategory v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["pricecategory_012345abcdef_123456"]},"pricecategory_map":{"anyOf":[{"type":"array","maxItems":200,"items":{"type":"object","properties":{"source":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque pricecategory v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["pricecategory_012345abcdef_123456"]},"target":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque pricecategory v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["pricecategory_012345abcdef_123456"]},"amount":{"anyOf":[{"type":"number","minimum":0,"exclusiveMaximum":100000000},{"type":"string","pattern":"^[0-9]{1,8}(?:\\.[0-9]{1,2})?$"}],"description":"Native numeric values or decimal text are checked using is_numeric and a regex over their PHP string form (1–8 integral digits, at most two decimal digits). Numeric JSON decimal precision remains a native check; no floating-point multipleOf assertion is invented."}},"additionalProperties":true,"required":["source","target"]}},{"type":"object","maxProperties":200,"additionalProperties":{"type":"object","properties":{"source":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque pricecategory v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["pricecategory_012345abcdef_123456"]},"target":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque pricecategory v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["pricecategory_012345abcdef_123456"]},"amount":{"anyOf":[{"type":"number","minimum":0,"exclusiveMaximum":100000000},{"type":"string","pattern":"^[0-9]{1,8}(?:\\.[0-9]{1,2})?$"}],"description":"Native numeric values or decimal text are checked using is_numeric and a regex over their PHP string form (1–8 integral digits, at most two decimal digits). Numeric JSON decimal precision remains a native check; no floating-point multipleOf assertion is invented."}},"additionalProperties":true,"required":["source","target"]}}],"description":"Native is_array/foreach accepts both a JSON list and keyed object; keys do not change source/target semantics."}},"additionalProperties":true,"required":["offer"]}},"additionalProperties":true},"examples":{"rebook":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"offer":"offer_012345abcdef_123456"}}},"copyWithPrices":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"offer":"offer_012345abcdef_123456","copy":true,"pricecategory":"pricecategory_012345abcdef_123456","pricecategory_map":[{"source":"pricecategory_012345abcdef_123456","target":"pricecategory_012345abcdef_123457","amount":"25.00"}]}}},"keyedPrices":{"summary":"Supported keyed mapping variant; native foreach consumes the entries.","value":{"data":{"offer":"offer_012345abcdef_123456","pricecategory_map":{"first":{"source":"pricecategory_012345abcdef_123456","target":"pricecategory_012345abcdef_123457"}}}}}}}}},"x-edoobox-action-contract":{"review":"booking-rebooking","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Booking.php","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","methods":["updateBooking"]},{"file":"application/models/v2/Bookingfunc.php","sha256":"de555a91da81398e3402d431d1dbda26eb1bc17019b2e44f17e854ced79762b7","methods":["_rebooking","resolveStaffRebookingPrices","_addBooking"]}],"preconditions":["Target offer differs from the current offer and belongs to this account.","Each map source must be used by a participant of the source booking/offer and appear only once; every target price is active and belongs to the target offer.","Mappings to the same target must agree on amount. Accounting locks and manual-invoice review can defer/reject the operation."],"errors":[{"error":"ED80100","error_code":"rebooking_invalid_target_data"},{"error":"ED80100","error_code":"rebooking_invalid_pricecategory"},{"error":"ED80100","meaning":"require.accounting_operation_in_progress or require.accounting_manual_review can require a fresh human review."}],"unresolved":["Participant mapping, accounting decisions, availability and downstream mutation results require native state. The expanded booking result remains open."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":402}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/send/booking_confirmation Buchungsbestätigung per E-Mail senden Sendet die Buchungsbestätigung an den Buchungsinhaber oder data.send_email; data.invoice kann eine bestehende Rechnung als PDF anhängen. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand. - Operation-ID: get_v2_booking_id_send_booking_confirmation - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-send-booking-confirmation-b424217fea9b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | | data | query | nein | JSON-Text mit optionalem Rechnungsanhang und abweichender Empfängeradresse. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"data","in":"query","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"enum":[false,null,""]}]},"send_email":{"anyOf":[{"type":"string","format":"email"},{"enum":[false,null,""]}],"description":"Abweichende gültige Empfängeradresse; false, null oder leerer Text verwenden den normalen Empfänger."}},"additionalProperties":true},"example":{"send_email":"alex@example.invalid","invoice":false}}},"description":"JSON-Text mit optionalem Rechnungsanhang und abweichender Empfängeradresse."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/send/booking_confirmation' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/send/booking_confirmation"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/send/booking_confirmation" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_send_booking_confirmation","summary":"Buchungsbestätigung per E-Mail senden","description":"Sendet die Buchungsbestätigung an den Buchungsinhaber oder data.send_email; data.invoice kann eine bestehende Rechnung als PDF anhängen. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"data","in":"query","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"enum":[false,null,""]}]},"send_email":{"anyOf":[{"type":"string","format":"email"},{"enum":[false,null,""]}],"description":"Abweichende gültige Empfängeradresse; false, null oder leerer Text verwenden den normalen Empfänger."}},"additionalProperties":true},"example":{"send_email":"alex@example.invalid","invoice":false}}},"description":"JSON-Text mit optionalem Rechnungsanhang und abweichender Empfängeradresse."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.send ist der Versanderfolg; beim Besuchsbestätigungsversand enthält participants die angeschriebenen Teilnahmen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"send":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Bookingfunc.php","line":2391,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":923,"lineEnd":931,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"The exact send branch denies nonempty resolved edident_readonly or effective book rights outside 3/4 (also accepting their string forms) with ED40340 before loading Bookingfunc or dispatching any mail helper."},"reason":"Sends/queues booking confirmation through shop_offerte::sendBestatigungsMail and the email outbox.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/13","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/13","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-native-action-authorization":{"short":"book","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Booking.php","method":"getBooking"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Bookingfunc.php","line":2412}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"The GET dispatcher checks booking access; the send handler additionally requires current capped book level 3 or 4 and rejects read-only logins before any mail/outbox dispatch.","dispatcherLevels":[1,3,4],"effectiveLevels":[3,4]}} ``` --- # GET /v2/booking/{id}/send/booking_reminder Buchungserinnerung per E-Mail senden Stösst den Versand der Buchungserinnerung für die ausgewählte Buchung an. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand. - Operation-ID: get_v2_booking_id_send_booking_reminder - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-send-booking-reminder-bf1c06381019/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/send/booking_reminder' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/send/booking_reminder"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/send/booking_reminder" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_send_booking_reminder","summary":"Buchungserinnerung per E-Mail senden","description":"Stösst den Versand der Buchungserinnerung für die ausgewählte Buchung an. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.send ist der Versanderfolg; beim Besuchsbestätigungsversand enthält participants die angeschriebenen Teilnahmen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"send":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Bookingfunc.php","line":2379,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":923,"lineEnd":931,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"The exact send branch denies nonempty resolved edident_readonly or effective book rights outside 3/4 (also accepting their string forms) with ED40340 before loading Bookingfunc or dispatching any mail helper."},"reason":"Sends/queues booking reminder through Offerte::sendNoteMail and the email outbox.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/14","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/14","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-native-action-authorization":{"short":"book","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Booking.php","method":"getBooking"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Bookingfunc.php","line":2400}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"The GET dispatcher checks booking access; the send handler additionally requires current capped book level 3 or 4 and rejects read-only logins before any mail/outbox dispatch.","dispatcherLevels":[1,3,4],"effectiveLevels":[3,4]}} ``` --- # GET /v2/booking/{id}/send/payment_reminder Zahlungserinnerung per E-Mail senden Sendet dem Buchungsinhaber die Saldo-/Zahlungserinnerung für das gebuchte Angebot. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand. - Operation-ID: get_v2_booking_id_send_payment_reminder - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-send-payment-reminder-cb742ccee403/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/send/payment_reminder' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/send/payment_reminder"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/send/payment_reminder" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_send_payment_reminder","summary":"Zahlungserinnerung per E-Mail senden","description":"Sendet dem Buchungsinhaber die Saldo-/Zahlungserinnerung für das gebuchte Angebot. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.send ist der Versanderfolg; beim Besuchsbestätigungsversand enthält participants die angeschriebenen Teilnahmen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"send":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Bookingfunc.php","line":2387,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":923,"lineEnd":931,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"The exact send branch denies nonempty resolved edident_readonly or effective book rights outside 3/4 (also accepting their string forms) with ED40340 before loading Bookingfunc or dispatching any mail helper."},"reason":"Sends/queues payment reminder through Teilnehmer::sendBalanceToUser and the email outbox.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/15","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/15","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-native-action-authorization":{"short":"book","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Booking.php","method":"getBooking"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Bookingfunc.php","line":2408}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"The GET dispatcher checks booking access; the send handler additionally requires current capped book level 3 or 4 and rejects read-only logins before any mail/outbox dispatch.","dispatcherLevels":[1,3,4],"effectiveLevels":[3,4]}} ``` --- # GET /v2/booking/{id}/send/visit_confirmation Teilnahmebestätigungen per E-Mail senden Sendet die Teilnahmebestätigung an alle Teilnehmer dieser Buchung mit gültiger E-Mail-Adresse; participants enthält die angeschriebenen öffentlichen Teilnehmer-IDs. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand. - Operation-ID: get_v2_booking_id_send_visit_confirmation - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-send-visit-confirmation-e70a8c2c46cb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/send/visit_confirmation' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/send/visit_confirmation"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/send/visit_confirmation" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_send_visit_confirmation","summary":"Teilnahmebestätigungen per E-Mail senden","description":"Sendet die Teilnahmebestätigung an alle Teilnehmer dieser Buchung mit gültiger E-Mail-Adresse; participants enthält die angeschriebenen öffentlichen Teilnehmer-IDs. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.send ist der Versanderfolg; beim Besuchsbestätigungsversand enthält participants die angeschriebenen Teilnahmen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","send":true,"participants":["participant_012345abcdef_123456"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"send":{"type":"boolean"},"participants":{"type":"array","items":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Bookingfunc.php","line":2383,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":923,"lineEnd":931,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"The exact send branch denies nonempty resolved edident_readonly or effective book rights outside 3/4 (also accepting their string forms) with ED40340 before loading Bookingfunc or dispatching any mail helper."},"reason":"Sends/queues visit confirmation through Teilnehmer::creatBesuchsMailOneUser with mail mode enabled.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/16","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/16","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-native-action-authorization":{"short":"book","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Booking.php","method":"getBooking"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Bookingfunc.php","line":2404}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"The GET dispatcher checks booking access; the send handler additionally requires current capped book level 3 or 4 and rejects read-only logins before any mail/outbox dispatch.","dispatcherLevels":[1,3,4],"effectiveLevels":[3,4]}} ``` --- # GET /v2/booking/{id}/ticket.pdf Tickets einer Buchung als PDF laden Rendert das zu dieser Buchung konfigurierte PDF und gibt dessen Bytes direkt zurück. Die Pfad-ID wählt eine Buchung des Kontos; ein optionales cacheToken ist nur ein zusätzlicher URL-Bestandteil und wird vom Handler nicht ausgewertet. Der Dateiaufruf versendet keine Bestätigungs-E-Mail. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden. - Operation-ID: get_v2_booking_id_ticket_pdf - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-ticket-pdf-e8f8ffba3db7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/ticket.pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/ticket.pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/ticket.pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_ticket_pdf","summary":"Tickets einer Buchung als PDF laden","description":"Rendert das zu dieser Buchung konfigurierte PDF und gibt dessen Bytes direkt zurück. Die Pfad-ID wählt eine Buchung des Kontos; ein optionales cacheToken ist nur ein zusätzlicher URL-Bestandteil und wird vom Handler nicht ausgewertet. Der Dateiaufruf versendet keine Bestätigungs-E-Mail. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Direkt gerendertes PDF; kontoabhängige Sichtbarkeit und Vorlagen bestimmen den Inhalt. Bei ungültigen Selektoren ist stattdessen eine JSON-Fehlerantwort möglich.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":908}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/{id}/ticket.pdf/{cacheToken} Tickets einer Buchung als PDF laden Rendert das zu dieser Buchung konfigurierte PDF und gibt dessen Bytes direkt zurück. Die Pfad-ID wählt eine Buchung des Kontos; ein optionales cacheToken ist nur ein zusätzlicher URL-Bestandteil und wird vom Handler nicht ausgewertet. Der Dateiaufruf versendet keine Bestätigungs-E-Mail. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden. - Operation-ID: get_v2_booking_id_ticket_pdf_cacheToken - Dokumentation: /docs/api/v2/operations/get-v2-booking-id-ticket-pdf-cachetoken-2f366b30178f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | | | cacheToken | path | ja | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque booking v2 ID returned by this account. Synthetic example is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${PATH_CACHETOKEN:?PATH_CACHETOKEN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/ticket.pdf/'"${PATH_CACHETOKEN}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/ticket.pdf/{cacheToken}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); path = path.replaceAll("{cacheToken}", encodeURIComponent(required("PATH_CACHETOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/ticket.pdf/{cacheToken}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) path = path.replace("{cacheToken}", urllib.parse.quote(os.environ["PATH_CACHETOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_id_ticket_pdf_cacheToken","summary":"Tickets einer Buchung als PDF laden","description":"Rendert das zu dieser Buchung konfigurierte PDF und gibt dessen Bytes direkt zurück. Die Pfad-ID wählt eine Buchung des Kontos; ein optionales cacheToken ist nur ein zusätzlicher URL-Bestandteil und wird vom Handler nicht ausgewertet. Der Dateiaufruf versendet keine Bestätigungs-E-Mail. Beim Laden einer PDF-Vorlage kann der Vorlagencache erneuert werden.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque booking v2 ID returned by this account. Synthetic example is not a real record.","examples":["booking_012345abcdef_123456"]}},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}],"responses":{"200":{"description":"Direkt gerendertes PDF; kontoabhängige Sichtbarkeit und Vorlagen bestimmen den Inhalt. Bei ungültigen Selektoren ist stattdessen eine JSON-Fehlerantwort möglich.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Booking.php","line":908,"reason":"The explicit PDF branch uses the booking ID filter and does not consume the reviewed final cAe timestamp cache segment."},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/17","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":908,"lineEnd":916,"sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","reason":"Exact ticket.pdf action calls scan::creatTicketPDF with backend true and PDF output."},"reason":"Renders an existing booking’s ticket PDF; lazy barcode image files are cache artifacts, not ticket issuance.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/17","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":908}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/user_add Benannten Teilnehmer oder unbenannte Plätze hinzufügen Fügt mit pricecategory entweder einen bestehenden user oder count_unnamed_slots hinzu. Beide Selektoren gleichzeitig sind unzulässig. Kontolimits, freie Plätze und Angebot-/Preiskategoriebeziehungen werden geprüft; die Rückgabe ergänzt den aktualisierten Buchungsstand. - Operation-ID: put_v2_booking_id_user_add - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-user-add-c09f5f5a48a4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "user": "user_012345abcdef_123456", "pricecategory": "pricecategory_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/user_add' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/user_add"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "user": "user_012345abcdef_123456", "pricecategory": "pricecategory_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/user_add" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"user\":\"user_012345abcdef_123456\",\"pricecategory\":\"pricecategory_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_user_add","summary":"Benannten Teilnehmer oder unbenannte Plätze hinzufügen","description":"Fügt mit pricecategory entweder einen bestehenden user oder count_unnamed_slots hinzu. Beide Selektoren gleichzeitig sind unzulässig. Kontolimits, freie Plätze und Angebot-/Preiskategoriebeziehungen werden geprüft; die Rückgabe ergänzt den aktualisierten Buchungsstand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"cancel":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand im eigenen data/error/type-Umschlag."},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["participant_012345abcdef_123456"]},{"const":false}]},"position":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque pricecategory v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["pricecategory_012345abcdef_123456"]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque user v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["user_012345abcdef_123456"]},"count_unnamed_slots":{"x-edoobox-canonicalType":"integer","description":"Native intval normalization; the resulting count must be positive and fit the account maximum and remaining booking slots."}},"additionalProperties":true,"oneOf":[{"required":["user"],"not":{"required":["count_unnamed_slots"]}},{"required":["count_unnamed_slots"],"not":{"required":["user"]}}],"required":["pricecategory"]}},"additionalProperties":true},"examples":{"named":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"user":"user_012345abcdef_123456","pricecategory":"pricecategory_012345abcdef_123456"}}},"unnamed":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"count_unnamed_slots":2,"pricecategory":"pricecategory_012345abcdef_123456"}}}}}}},"x-edoobox-action-contract":{"review":"booking-user-add","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Booking.php","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","methods":["updateBooking"]},{"file":"application/models/v2/Bookingfunc.php","sha256":"de555a91da81398e3402d431d1dbda26eb1bc17019b2e44f17e854ced79762b7","methods":["_addUnnamedUserBooking"]}],"preconditions":["Booking, user and selected price must belong to the selected account and valid booking/offer relations.","Unnamed-slot limits are account-dependent and are checked after intval normalization."],"errors":[{"error":"ED80100"},{"error":"ED80100","error_code":"max_users_per_booking_exceeded"},{"error":"ED80100","error_code":"not_enough_slots"}],"unresolved":["Account-specific slot maximum, current occupied seats and downstream Offerte membership/pricing validation remain native."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":354}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/booking/{id}/user_status Teilnehmerposition aktivieren oder abmelden Setzt status für eine konkrete Teilnehmerposition. participant, ein eindeutig auflösbarer user oder eine gespeicherte position wählen das Ziel; nicht eindeutige Benutzerzuordnungen werden abgelehnt. Die Aktion kann die gesamte Buchung stornieren und meldet dies über cancel; Preis- und Buchungsfolgen laufen im nativen Ablauf. - Operation-ID: put_v2_booking_id_user_status - Dokumentation: /docs/api/v2/operations/put-v2-booking-id-user-status-536f48bcc009/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false, "participant": "participant_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/booking/'"${PATH_ID}"'/user_status' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/{id}/user_status"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false, "participant": "participant_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/{id}/user_status" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false,\"participant\":\"participant_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_booking_id_user_status","summary":"Teilnehmerposition aktivieren oder abmelden","description":"Setzt status für eine konkrete Teilnehmerposition. participant, ein eindeutig auflösbarer user oder eine gespeicherte position wählen das Ziel; nicht eindeutige Benutzerzuordnungen werden abgelehnt. Die Aktion kann die gesamte Buchung stornieren und meldet dies über cancel; Preis- und Buchungsfolgen laufen im nativen Ablauf.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque booking v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["booking_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]},"update":{"type":"boolean"},"cancel":{"type":"boolean"},"booking":{"type":"object","properties":{},"additionalProperties":true,"description":"Erweiterter Buchungsstand im eigenen data/error/type-Umschlag."},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["participant_012345abcdef_123456"]},{"const":false}]},"position":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque participant v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["participant_012345abcdef_123456"]},{"enum":[false,null,""]}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque user v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["user_012345abcdef_123456"]},{"enum":[false,null,""]}]},"position":{"anyOf":[{"type":"string"},{"const":false},{"type":"null"}]},"status":{"x-edoobox-canonicalType":"boolean","description":"PHP loose boolean comparison selects the requested participant status."}},"additionalProperties":true,"anyOf":[{"required":["participant"],"properties":{"participant":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque participant v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["participant_012345abcdef_123456"]}}},{"required":["user"],"properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Existing opaque user v2 ID from the selected account. The native action checks account/resource relations; a syntactically matching synthetic ID does not establish existence.","examples":["user_012345abcdef_123456"]}}},{"required":["position"],"properties":{"position":{"type":"string","minLength":1}}}],"required":["status"]}},"additionalProperties":true},"examples":{"participant":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"status":false,"participant":"participant_012345abcdef_123456"}}},"user":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"status":false,"user":"user_012345abcdef_123456"}}}}}}},"x-edoobox-action-contract":{"review":"booking-user_status","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Booking.php","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5","methods":["updateBooking","resolveBookingParticipantTarget","getSerializedBookingPositionSlot"]}],"preconditions":["Participant/user/position must resolve inside this account-owned booking. Multiple matching participant/serialized slots require an explicit selection.","A serialized position is usable only under the action-specific inactive-slot rules in resolveBookingParticipantTarget."],"errors":[{"error":"ED80100","error_code":"participant_not_found"},{"error":"ED80100","error_code":"participant_ambiguous"},{"error":"ED80100","error_code":"booking_not_found"}],"unresolved":["Current slot state, user/participant disambiguation and Offerte effects remain native; position format is intentionally open."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"updateBooking","line":222,"branch":{"file":"application/models/v2/Booking.php","line":290}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/list Buchungen und Buchungsstatus lesen Liest Buchungen des ausgewählten Kontos mit Angebot, Buchungsinhaber, Wartelisten-/Stornierungsstatus und optionalen Transaktionen beziehungsweise Teilnehmern. Eine Pfad-ID wird als erster id-Filter behandelt; ohne Pfad-ID begrenzen filter, fields, order und limit die Sammlung. Auch eine einzelne ID liefert das allgemeine Sammlungsformat. - Operation-ID: get_v2_booking_list - Dokumentation: /docs/api/v2/operations/get-v2-booking-list-98e44cb72be3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_list","summary":"Buchungen und Buchungsstatus lesen","description":"Liest Buchungen des ausgewählten Kontos mit Angebot, Buchungsinhaber, Wartelisten-/Stornierungsstatus und optionalen Transaktionen beziehungsweise Teilnehmern. Eine Pfad-ID wird als erster id-Filter behandelt; ohne Pfad-ID begrenzen filter, fields, order und limit die Sammlung. Auch eine einzelne ID liefert das allgemeine Sammlungsformat.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"booking_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"booking_012345abcdef_123456":{"id":"booking_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["booking_012345abcdef_123456"]},"time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32,"anyOf":[{"type":"string"},{"const":false}]},"offer":{"description":"Existing offer in the selected account; required by the native booking-create hook.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"owner":{"description":"Existing booking owner (user v2 ID); required for booking creation, immutable on ordinary update.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["owner_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"canceled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":72},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":82},"waiting_list":{"description":"Create as a waiting-list booking; lesson-based offers reject this mode in Bookingfunc::_addBooking.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93},"label":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":103,"type":"string"},"users":{"type":"array","items":{"type":"object","properties":{"participant":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"pricecategory":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["participant","user","pricecategory"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":117,"reason":"Response builds participant/user/pricecategory objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]},"transactions":{"type":"array","items":{"type":"object","properties":{"transaction":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"number":{"type":"number"},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true,"required":["transaction","number","amount","currency"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Booking.php","line":139,"reason":"Response builds transaction/number/amount/currency objects inside an array.","sha256":"78bb2e6d2875243f1267a8724ab29e406b7c93dd351c50f6a270f9c184ae81b5"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/list/accountconnect Buchungen für Account Connect exportieren Exportiert Buchungsdaten für den Zeitraum from bis to; to muss nach from liegen. format ist ods, xls, xlsx oder csv. Die Antwort ist ein Dateidownload, kein JSON-Datensatz; bei mehr als 6000 Exportzeilen wird automatisch CSV verwendet. - Operation-ID: get_v2_booking_list_accountconnect - Dokumentation: /docs/api/v2/operations/get-v2-booking-list-accountconnect-39eb0347949f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | from | query | ja | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | to | query | ja | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | format | query | ja | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"from","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"Von dP parsebarer Zeitpunkt; to muss strikt nach from liegen."},"example":"2026-10-01T00:00:00+02:00"},{"name":"to","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"Von dP parsebarer Zeitpunkt; to muss strikt nach from liegen."},"example":"2026-11-01T00:00:00+01:00"},{"name":"format","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","enum":["ods","xls","xlsx","csv"]},"example":"xlsx"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FROM:?QUERY_FROM setzen}" : "${QUERY_TO:?QUERY_TO setzen}" : "${QUERY_FORMAT:?QUERY_FORMAT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/list/accountconnect' \ --url-query 'from='"${QUERY_FROM}" \ --url-query 'to='"${QUERY_TO}" \ --url-query 'format='"${QUERY_FORMAT}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/list/accountconnect"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("from", required("QUERY_FROM")); url.searchParams.append("to", required("QUERY_TO")); url.searchParams.append("format", required("QUERY_FORMAT")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/list/accountconnect" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("from", os.environ["QUERY_FROM"]), ("to", os.environ["QUERY_TO"]), ("format", os.environ["QUERY_FORMAT"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_list_accountconnect","summary":"Buchungen für Account Connect exportieren","description":"Exportiert Buchungsdaten für den Zeitraum from bis to; to muss nach from liegen. format ist ods, xls, xlsx oder csv. Die Antwort ist ein Dateidownload, kein JSON-Datensatz; bei mehr als 6000 Exportzeilen wird automatisch CSV verwendet.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"from","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"Von dP parsebarer Zeitpunkt; to muss strikt nach from liegen."},"example":"2026-10-01T00:00:00+02:00"},{"name":"to","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"Von dP parsebarer Zeitpunkt; to muss strikt nach from liegen."},"example":"2026-11-01T00:00:00+01:00"},{"name":"format","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","enum":["ods","xls","xlsx","csv"]},"example":"xlsx"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Tabellen-/CSV-Datei entsprechend format; über 6000 Zeilen wird CSV erzwungen. Ungültige Zeitraumparameter liefern JSON mit require.","content":{"application/vnd.oasis.opendocument.spreadsheet":{"schema":{"type":"string","format":"binary"}},"application/vnd.ms-excel":{"schema":{"type":"string","format":"binary"}},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"schema":{"type":"string","format":"binary"}},"text/csv":{"schema":{"type":"string","format":"binary"}},"application/json":{"example":{"success":true,"error":false,"data":{}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Export formats stream directly to response."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":933}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/list/data Vollständige Buchungsübersicht lesen Liest den gespeicherten Buchungsstand mit Teilnehmerpositionen, Preisen, Zusatzleistungen, Antworten, Zahlungen, Rechnungsadresse, Rechts- und Stornierungsinformationen. Genau eine bestehende Buchung wird über die Pfad-ID oder den ersten filter-Eintrag mit property=id ausgewählt; dies ist keine Listenabfrage. - Operation-ID: get_v2_booking_list_data - Dokumentation: /docs/api/v2/operations/get-v2-booking-list-data-58949bf0c846/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Die erste Filterposition muss die konkrete Buchungs-ID enthalten. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Die erste Filterposition muss die konkrete Buchungs-ID enthalten.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}],"prefixItems":[{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}],"minItems":1},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/list/data' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/list/data"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/list/data" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_list_data","summary":"Vollständige Buchungsübersicht lesen","description":"Liest den gespeicherten Buchungsstand mit Teilnehmerpositionen, Preisen, Zusatzleistungen, Antworten, Zahlungen, Rechnungsadresse, Rechts- und Stornierungsinformationen. Genau eine bestehende Buchung wird über die Pfad-ID oder den ersten filter-Eintrag mit property=id ausgewählt; dies ist keine Listenabfrage.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Die erste Filterposition muss die konkrete Buchungs-ID enthalten.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}],"prefixItems":[{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}],"minItems":1},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erweiterte fachliche Rückgabe in data; die aufgeführten Sammlungen können leer sein und enthalten ausschliesslich öffentliche Referenzen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"transactions":[{"id":"transaction_012345abcdef_123456","number":"2026-1001","amount":120,"currency":"CHF"}],"users":[{"active":true,"participant":"participant_012345abcdef_123456","position":"person","user":"user_012345abcdef_123456","pricecategory":"pricecategory_012345abcdef_123456","cert_visible":false}],"payments":[],"promotion":false,"multi_offer":false,"dates":[],"lessons_booking":false,"invoice_address":{"active":false,"b2b":false,"data0":"","data1":"","data2":"","data3":"","data4":"","data5":"","data7":"","data8":""},"label":false,"ip_address":"192.0.2.1","offer":"offer_012345abcdef_123456","questiongroup":[],"multi_offer_questiongroup":[],"deliverables":[],"pricecategory":{"pricecategory_012345abcdef_123456":{"id":"pricecategory_012345abcdef_123456","count":1,"name":"Standard","amount":120,"default":true,"offer":"offer_012345abcdef_123456"}},"legaltexts":[],"cancellation_conditions":false,"cancellation_conditions_history":[],"cancellation_fee_preview":false,"fees":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"transactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"]},"number":{"type":["number","string"]},"amount":{"type":"number"},"currency":{"type":"string"}},"additionalProperties":true}},"users":{"type":"array","items":{"type":"object","properties":{"active":{"type":"boolean"},"participant":{"anyOf":[{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"]},{"const":false}]},"position":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"cert_visible":{"type":"boolean"},"unnamed":{"type":"boolean"},"multi_offer":{"type":"array","items":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]}},"additionalProperties":true}}},"additionalProperties":true}},"payments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","examples":["paytrans_012345abcdef_123456"]},"refundable":{"type":"boolean"},"payment":{"type":"object","properties":{"type":{"type":"string"},"token":{"type":"string"},"currency":{"type":"string"},"amount":{"type":"number"},"refound":{"anyOf":[{"type":"object","properties":{"repaid_amount":{"type":"number"},"refundable_amount":{"type":"number"}},"additionalProperties":true},{"const":false}]},"status":{"type":"integer"},"date":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},"additionalProperties":true}},"additionalProperties":true}},"promotion":{"anyOf":[{"type":"object","properties":{"code":{"type":"string"},"currency":{"type":"string"},"active":{"type":"boolean"},"value":{"type":["number","string"]},"id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"]},"minimum_booking_amount":{"type":["number","string"]}},"additionalProperties":true},{"const":false}]},"multi_offer":{"type":"boolean"},"dates":{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","examples":["date_012345abcdef_123456"]}},"lessons_booking":{"type":"boolean"},"invoice_address":{"type":"object","properties":{"data0":{"type":"string"},"data1":{"type":"string"},"data2":{"type":"string"},"data3":{"type":"string"},"data4":{"type":"string"},"data5":{"type":"string"},"data6":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","examples":["country_012345abcdef_123456"]},{"const":false}]},"data7":{"type":"string"},"data8":{"type":"string"},"active":{"type":"boolean"},"b2b":{"type":"boolean"}},"additionalProperties":true},"label":{"anyOf":[{"type":"string"},{"const":false}]},"ip_address":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"questiongroup":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"multi_offer_questiongroup":{"type":["object","array"],"additionalProperties":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"title":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"answer":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","examples":["question_012345abcdef_123456"]},"name":{"type":"string"},"value":{"description":"Gespeicherte Freitext-/Zahlantwort oder öffentliche question-ID der Auswahloption."}},"additionalProperties":true}},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"items":{}},"deliverables":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"detail":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"number"}},"additionalProperties":true},"count":{"type":["number","string"]},"amount":{"type":["number","string"]},"currency":{"type":"string"},"vat":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"]},"vat_percent":{"type":["number","string"]},"vat_number":{"type":"string"}},"additionalProperties":true},{"const":false}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"detail":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"number"}},"additionalProperties":true},"count":{"type":["number","string"]},"amount":{"type":["number","string"]},"currency":{"type":"string"},"vat":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"]},"vat_percent":{"type":["number","string"]},"vat_number":{"type":"string"}},"additionalProperties":true},{"const":false}]}},"additionalProperties":true}}]},"pricecategory":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"count":{"type":"integer"},"name":{"type":"string"},"amount":{"type":"number"},"default":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"]},"count":{"type":"integer"},"name":{"type":"string"},"amount":{"type":"number"},"default":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}}]},"legaltexts":{"type":"array","items":{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"]},"legalversion":{"anyOf":[{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","examples":["legalversion_012345abcdef_123456"]},{"const":false}]},"accepted":{"type":"boolean"},"require":{"type":"boolean"}},"additionalProperties":true}},"cancellation_conditions":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Snapshot mit kontoabhängigen Stornierungs- und Widerrufsregeln."},{"const":false}]},"cancellation_conditions_history":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Historieneinträge mit Akteur und den damaligen Snapshotwerten."}},"cancellation_fee_preview":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true,"description":"Aktuelle Gebührenvorschau gemäss Stornierungsbedingungen und Zahlungsstand."},{"const":false}]},"fees":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Öffentliche Gebührenobjekte dieser Buchung."}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":787}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/list/labelexport Buchungsreferenzen exportieren Exportiert Buchungsdaten für den Zeitraum from bis to; to muss nach from liegen. format ist ods, xls, xlsx oder csv. Die Antwort ist ein Dateidownload, kein JSON-Datensatz; bei mehr als 6000 Exportzeilen wird automatisch CSV verwendet. - Operation-ID: get_v2_booking_list_labelexport - Dokumentation: /docs/api/v2/operations/get-v2-booking-list-labelexport-9e22f0458912/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | from | query | ja | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | to | query | ja | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | format | query | ja | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"from","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"Von dP parsebarer Zeitpunkt; to muss strikt nach from liegen."},"example":"2026-10-01T00:00:00+02:00"},{"name":"to","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"Von dP parsebarer Zeitpunkt; to muss strikt nach from liegen."},"example":"2026-11-01T00:00:00+01:00"},{"name":"format","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","enum":["ods","xls","xlsx","csv"]},"example":"xlsx"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FROM:?QUERY_FROM setzen}" : "${QUERY_TO:?QUERY_TO setzen}" : "${QUERY_FORMAT:?QUERY_FORMAT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/list/labelexport' \ --url-query 'from='"${QUERY_FROM}" \ --url-query 'to='"${QUERY_TO}" \ --url-query 'format='"${QUERY_FORMAT}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/list/labelexport"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("from", required("QUERY_FROM")); url.searchParams.append("to", required("QUERY_TO")); url.searchParams.append("format", required("QUERY_FORMAT")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/list/labelexport" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("from", os.environ["QUERY_FROM"]), ("to", os.environ["QUERY_TO"]), ("format", os.environ["QUERY_FORMAT"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_list_labelexport","summary":"Buchungsreferenzen exportieren","description":"Exportiert Buchungsdaten für den Zeitraum from bis to; to muss nach from liegen. format ist ods, xls, xlsx oder csv. Die Antwort ist ein Dateidownload, kein JSON-Datensatz; bei mehr als 6000 Exportzeilen wird automatisch CSV verwendet.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"from","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"Von dP parsebarer Zeitpunkt; to muss strikt nach from liegen."},"example":"2026-10-01T00:00:00+02:00"},{"name":"to","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","description":"Von dP parsebarer Zeitpunkt; to muss strikt nach from liegen."},"example":"2026-11-01T00:00:00+01:00"},{"name":"format","in":"query","required":true,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"type":"string","enum":["ods","xls","xlsx","csv"]},"example":"xlsx"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Tabellen-/CSV-Datei entsprechend format; über 6000 Zeilen wird CSV erzwungen. Ungültige Zeitraumparameter liefern JSON mit require.","content":{"application/vnd.oasis.opendocument.spreadsheet":{"schema":{"type":"string","format":"binary"}},"application/vnd.ms-excel":{"schema":{"type":"string","format":"binary"}},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"schema":{"type":"string","format":"binary"}},"text/csv":{"schema":{"type":"string","format":"binary"}},"application/json":{"example":{"success":true,"error":false,"data":{}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Export formats stream directly to response."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Booking.php","line":936}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/list/send/booking_confirmation Buchungsbestätigung per E-Mail senden Sendet die Buchungsbestätigung an den Buchungsinhaber oder data.send_email; data.invoice kann eine bestehende Rechnung als PDF anhängen. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand. - Operation-ID: get_v2_booking_list_send_booking_confirmation - Dokumentation: /docs/api/v2/operations/get-v2-booking-list-send-booking-confirmation-8aef3554973e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | data | query | nein | JSON-Text mit optionalem Rechnungsanhang und abweichender Empfängeradresse. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"data","in":"query","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"enum":[false,null,""]}]},"send_email":{"anyOf":[{"type":"string","format":"email"},{"enum":[false,null,""]}],"description":"Abweichende gültige Empfängeradresse; false, null oder leerer Text verwenden den normalen Empfänger."}},"additionalProperties":true},"example":{"send_email":"alex@example.invalid","invoice":false}}},"description":"JSON-Text mit optionalem Rechnungsanhang und abweichender Empfängeradresse."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/list/send/booking_confirmation' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/list/send/booking_confirmation"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/list/send/booking_confirmation" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_list_send_booking_confirmation","summary":"Buchungsbestätigung per E-Mail senden","description":"Sendet die Buchungsbestätigung an den Buchungsinhaber oder data.send_email; data.invoice kann eine bestehende Rechnung als PDF anhängen. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"data","in":"query","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"enum":[false,null,""]}]},"send_email":{"anyOf":[{"type":"string","format":"email"},{"enum":[false,null,""]}],"description":"Abweichende gültige Empfängeradresse; false, null oder leerer Text verwenden den normalen Empfänger."}},"additionalProperties":true},"example":{"send_email":"alex@example.invalid","invoice":false}}},"description":"JSON-Text mit optionalem Rechnungsanhang und abweichender Empfängeradresse."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.send ist der Versanderfolg; beim Besuchsbestätigungsversand enthält participants die angeschriebenen Teilnahmen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"send":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":923},"reason":"Invokes booking/reminder/payment/visit mail sending."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Bookingfunc.php","line":2412}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/list/send/booking_reminder Buchungserinnerung per E-Mail senden Stösst den Versand der Buchungserinnerung für die ausgewählte Buchung an. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand. - Operation-ID: get_v2_booking_list_send_booking_reminder - Dokumentation: /docs/api/v2/operations/get-v2-booking-list-send-booking-reminder-6fb00eb2bb59/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/list/send/booking_reminder' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/list/send/booking_reminder"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/list/send/booking_reminder" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_list_send_booking_reminder","summary":"Buchungserinnerung per E-Mail senden","description":"Stösst den Versand der Buchungserinnerung für die ausgewählte Buchung an. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.send ist der Versanderfolg; beim Besuchsbestätigungsversand enthält participants die angeschriebenen Teilnahmen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"send":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":923},"reason":"Invokes booking/reminder/payment/visit mail sending."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Bookingfunc.php","line":2400}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/list/send/payment_reminder Zahlungserinnerung per E-Mail senden Sendet dem Buchungsinhaber die Saldo-/Zahlungserinnerung für das gebuchte Angebot. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand. - Operation-ID: get_v2_booking_list_send_payment_reminder - Dokumentation: /docs/api/v2/operations/get-v2-booking-list-send-payment-reminder-fcc6082d122b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/list/send/payment_reminder' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/list/send/payment_reminder"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/list/send/payment_reminder" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_list_send_payment_reminder","summary":"Zahlungserinnerung per E-Mail senden","description":"Sendet dem Buchungsinhaber die Saldo-/Zahlungserinnerung für das gebuchte Angebot. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.send ist der Versanderfolg; beim Besuchsbestätigungsversand enthält participants die angeschriebenen Teilnahmen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"send":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":923},"reason":"Invokes booking/reminder/payment/visit mail sending."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Bookingfunc.php","line":2408}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/booking/list/send/visit_confirmation Teilnahmebestätigungen per E-Mail senden Sendet die Teilnahmebestätigung an alle Teilnehmer dieser Buchung mit gültiger E-Mail-Adresse; participants enthält die angeschriebenen öffentlichen Teilnehmer-IDs. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand. - Operation-ID: get_v2_booking_list_send_visit_confirmation - Dokumentation: /docs/api/v2/operations/get-v2-booking-list-send-visit-confirmation-a168824a8a63/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/booking/list/send/visit_confirmation' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/booking/list/send/visit_confirmation"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/booking/list/send/visit_confirmation" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_booking_list_send_visit_confirmation","summary":"Teilnahmebestätigungen per E-Mail senden","description":"Sendet die Teilnahmebestätigung an alle Teilnehmer dieser Buchung mit gültiger E-Mail-Adresse; participants enthält die angeschriebenen öffentlichen Teilnehmer-IDs. Dieser GET führt einen Versand aus und benötigt neben GET auch Schreibrecht für Buchungen. Bei /list muss filter eine konkrete Buchungs-ID enthalten; kein Massenversand.","tags":["booking"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Wählt genau eine Buchung für den Versand; zusätzliche IDs werden nicht als Massenversand verarbeitet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"allOf":[{"contains":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"additionalProperties":true,"required":["property","value"]}}]},"example":[{"property":"id","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.send ist der Versanderfolg; beim Besuchsbestätigungsversand enthält participants die angeschriebenen Teilnahmen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"booking_012345abcdef_123456","send":true,"participants":["participant_012345abcdef_123456"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"send":{"type":"boolean"},"participants":{"type":"array","items":{"type":"string","pattern":"^participant_[a-f0-9]{12}_-?[0-9]+$","examples":["participant_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Booking.php","line":923},"reason":"Invokes booking/reminder/payment/visit mail sending."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Booking.php","method":"getBooking","line":556,"branch":{"file":"application/models/v2/Bookingfunc.php","line":2404}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"booking","short":"book","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/bookingqueue Warteraum-Platz freigeben Gibt den Eintrag über queue_token oder admission_token frei und lässt gegebenenfalls wartende Einträge nachrücken. Nur completed=true als echtes JSON-Boolean setzt completed; sonst cancelled. DELETE auf die Basisroute braucht zusätzlich ein oberstes id-Feld für den Dispatcher; der Platz wird anhand des Tokens bestimmt. - Operation-ID: delete_v2_bookingqueue - Dokumentation: /docs/api/v2/operations/delete-v2-bookingqueue-38e24c0185f0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bookingqueue_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "completed": false }, "id": "queue" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/bookingqueue' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bookingqueue"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "completed": false }, "id": "queue" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bookingqueue" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"queue_token\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"completed\":false},\"id\":\"queue\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_bookingqueue","summary":"Warteraum-Platz freigeben","description":"Gibt den Eintrag über queue_token oder admission_token frei und lässt gegebenenfalls wartende Einträge nachrücken. Nur completed=true als echtes JSON-Boolean setzt completed; sonst cancelled. DELETE auf die Basisroute braucht zusätzlich ein oberstes id-Feld für den Dispatcher; der Platz wird anhand des Tokens bestimmt.","tags":["bookingqueue"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bookingqueue_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"released":true,"status":"cancelled","mode":"booking","offer":"offer_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"released":{"type":"boolean"},"status":{"type":"string","enum":["completed","cancelled"]},"mode":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"admission_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"completed":{"x-edoobox-canonicalType":"boolean","description":"Nur echtes JSON true setzt completed; andere Werte setzen cancelled."}},"additionalProperties":true,"anyOf":[{"required":["queue_token"]},{"required":["admission_token"]}]},"id":{"description":"Dispatcher-Kompatibilitätsfeld; der Eintrag wird über Tokens ausgewählt.","examples":["queue"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Warteraum-Platz freigeben","value":{"data":{"queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","completed":false},"id":"queue"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingqueue.php","method":"deleteBookingqueue","line":33},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bookingqueue","short":null,"checkedByDispatcher":false,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/bookingqueue Status eines Warteraum-Eintrags prüfen Liest Status, Position, erwartete Wartezeit, freie Plätze und Ablaufzeit des Eintrags mit queue_token. Der Aufruf bereinigt zuvor abgelaufene Einträge und kann freie Plätze an Wartende vergeben; er ist deshalb trotz Statusabfrage zustandsändernd. GET übermittelt data[queue_token] als PHP-Bracket-Query, POST verwendet data.queue_token. - Operation-ID: get_v2_bookingqueue - Dokumentation: /docs/api/v2/operations/get-v2-bookingqueue-0d06f1118ef4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | data[queue_token] | query | ja | Bracket-Query wird als data.queue_token gelesen; JSON-Text in data wird beim GET nicht dekodiert. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"in":"query","name":"data[queue_token]","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"example":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","description":"Bracket-Query wird als data.queue_token gelesen; JSON-Text in data wird beim GET nicht dekodiert."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_DATA_QUEUE_TOKEN_:?QUERY_DATA_QUEUE_TOKEN_ setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/bookingqueue' \ --url-query 'data[queue_token]='"${QUERY_DATA_QUEUE_TOKEN_}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bookingqueue"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("data[queue_token]", required("QUERY_DATA_QUEUE_TOKEN_")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bookingqueue" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("data[queue_token]", os.environ["QUERY_DATA_QUEUE_TOKEN_"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_bookingqueue","summary":"Status eines Warteraum-Eintrags prüfen","description":"Liest Status, Position, erwartete Wartezeit, freie Plätze und Ablaufzeit des Eintrags mit queue_token. Der Aufruf bereinigt zuvor abgelaufene Einträge und kann freie Plätze an Wartende vergeben; er ist deshalb trotz Statusabfrage zustandsändernd. GET übermittelt data[queue_token] als PHP-Bracket-Query, POST verwendet data.queue_token.","tags":["bookingqueue"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"in":"query","name":"data[queue_token]","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"example":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","description":"Bracket-Query wird als data.queue_token gelesen; JSON-Text in data wird beim GET nicht dekodiert."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält den Wartestatus; bei join können bei geschlossenen/vollen Räumen Eintragstokens und Positionsfelder fehlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"waiting_room_required":true,"status":"waiting","mode":"booking","offer":"offer_012345abcdef_123456","scope":"account","queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","position":2,"estimated_wait_seconds":60,"active_limit":100,"free_places":12,"expires_at":1792400090}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"waiting_room_required":{"type":"boolean"},"status":{"type":"string"},"mode":{"type":"string","enum":["booking","waitlist"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"scope":{"type":"string","enum":["account","offer"]},"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"position":{"type":"integer"},"estimated_wait_seconds":{"type":"integer"},"active_limit":{"type":"integer"},"free_places":{"type":["integer","null"]},"expires_at":{"type":"integer"},"admission_token":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/models/v2/Bookingqueuefunc.php","line":173,"reason":"Dedicated queue token plus authenticated account checks; GET also cleans expired queue entries."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Bookingqueuefunc.php","line":180},"reason":"Status polling cleans expired entries and can admit the next queue member."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingqueue.php","method":"getBookingqueue","line":39},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bookingqueue","short":null,"checkedByDispatcher":false,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/bookingqueue Warteraum-Eintrag am Leben halten Verlängert den gültigen Warteraum-Eintrag über queue_token und wechselt admitted zu active. Abgelaufene Einträge werden vorher bereinigt und können durch einen Heartbeat nicht wiederbelebt werden. Beim PUT auf die Basisroute benötigt der gemeinsame Dispatcher zusätzlich ein oberstes id-Feld als Kompatibilitätsselektor; der Warteraum selbst verwendet queue_token. - Operation-ID: put_v2_bookingqueue - Dokumentation: /docs/api/v2/operations/put-v2-bookingqueue-aacb426db65d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, "id": "queue" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/bookingqueue' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bookingqueue"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, "id": "queue" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bookingqueue" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"queue_token\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"},\"id\":\"queue\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_bookingqueue","summary":"Warteraum-Eintrag am Leben halten","description":"Verlängert den gültigen Warteraum-Eintrag über queue_token und wechselt admitted zu active. Abgelaufene Einträge werden vorher bereinigt und können durch einen Heartbeat nicht wiederbelebt werden. Beim PUT auf die Basisroute benötigt der gemeinsame Dispatcher zusätzlich ein oberstes id-Feld als Kompatibilitätsselektor; der Warteraum selbst verwendet queue_token.","tags":["bookingqueue"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält den Wartestatus; bei join können bei geschlossenen/vollen Räumen Eintragstokens und Positionsfelder fehlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"waiting_room_required":true,"status":"waiting","mode":"booking","offer":"offer_012345abcdef_123456","scope":"account","queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","position":2,"estimated_wait_seconds":60,"active_limit":100,"free_places":12,"expires_at":1792400090}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"waiting_room_required":{"type":"boolean"},"status":{"type":"string"},"mode":{"type":"string","enum":["booking","waitlist"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"scope":{"type":"string","enum":["account","offer"]},"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"position":{"type":"integer"},"estimated_wait_seconds":{"type":"integer"},"active_limit":{"type":"integer"},"free_places":{"type":["integer","null"]},"expires_at":{"type":"integer"},"admission_token":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true,"required":["queue_token"]},"id":{"description":"Dispatcher-Kompatibilitätsfeld; wird vom Warteraum nicht ausgewertet.","examples":["queue"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Warteraum-Eintrag am Leben halten","value":{"data":{"queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"id":"queue"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingqueue.php","method":"updateBookingqueue","line":27},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bookingqueue","short":null,"checkedByDispatcher":false,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/bookingqueue/list/heartbeat Warteraum-Eintrag am Leben halten Verlängert den gültigen Warteraum-Eintrag über queue_token und wechselt admitted zu active. Abgelaufene Einträge werden vorher bereinigt und können durch einen Heartbeat nicht wiederbelebt werden. Beim PUT auf die Basisroute benötigt der gemeinsame Dispatcher zusätzlich ein oberstes id-Feld als Kompatibilitätsselektor; der Warteraum selbst verwendet queue_token. - Operation-ID: post_v2_bookingqueue_list_heartbeat - Dokumentation: /docs/api/v2/operations/post-v2-bookingqueue-list-heartbeat-066663f3ec18/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/bookingqueue/list/heartbeat' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bookingqueue/list/heartbeat"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bookingqueue/list/heartbeat" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"queue_token\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_bookingqueue_list_heartbeat","summary":"Warteraum-Eintrag am Leben halten","description":"Verlängert den gültigen Warteraum-Eintrag über queue_token und wechselt admitted zu active. Abgelaufene Einträge werden vorher bereinigt und können durch einen Heartbeat nicht wiederbelebt werden. Beim PUT auf die Basisroute benötigt der gemeinsame Dispatcher zusätzlich ein oberstes id-Feld als Kompatibilitätsselektor; der Warteraum selbst verwendet queue_token.","tags":["bookingqueue"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält den Wartestatus; bei join können bei geschlossenen/vollen Räumen Eintragstokens und Positionsfelder fehlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"waiting_room_required":true,"status":"waiting","mode":"booking","offer":"offer_012345abcdef_123456","scope":"account","queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","position":2,"estimated_wait_seconds":60,"active_limit":100,"free_places":12,"expires_at":1792400090}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"waiting_room_required":{"type":"boolean"},"status":{"type":"string"},"mode":{"type":"string","enum":["booking","waitlist"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"scope":{"type":"string","enum":["account","offer"]},"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"position":{"type":"integer"},"estimated_wait_seconds":{"type":"integer"},"active_limit":{"type":"integer"},"free_places":{"type":["integer","null"]},"expires_at":{"type":"integer"},"admission_token":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true,"required":["queue_token"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Warteraum-Eintrag am Leben halten","value":{"data":{"queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingqueue.php","method":"postBookingqueue","line":11,"branch":{"file":"application/models/v2/Bookingqueue.php","line":15}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bookingqueue","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/bookingqueue/list/join Buchungs-Warteraum betreten Tritt für offer dem Warteraum bei oder verwendet einen vorhandenen Eintrag derselben session wieder. mode=waitlist benötigt eine aktivierte Warteliste, sonst gilt booking. Die Kontokapazität beziehungsweise Angebotskapazität bestimmt admitted, waiting oder einen gesperrten Status; queue_token wird für Folgeaufrufe und admission_token nach Zulassung ausgegeben. - Operation-ID: post_v2_bookingqueue_list_join - Dokumentation: /docs/api/v2/operations/post-v2-bookingqueue-list-join-9312afcc89cb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "mode": "booking", "session": "browser-session-demo" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/bookingqueue/list/join' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bookingqueue/list/join"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "mode": "booking", "session": "browser-session-demo" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bookingqueue/list/join" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"mode\":\"booking\",\"session\":\"browser-session-demo\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_bookingqueue_list_join","summary":"Buchungs-Warteraum betreten","description":"Tritt für offer dem Warteraum bei oder verwendet einen vorhandenen Eintrag derselben session wieder. mode=waitlist benötigt eine aktivierte Warteliste, sonst gilt booking. Die Kontokapazität beziehungsweise Angebotskapazität bestimmt admitted, waiting oder einen gesperrten Status; queue_token wird für Folgeaufrufe und admission_token nach Zulassung ausgegeben.","tags":["bookingqueue"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält den Wartestatus; bei join können bei geschlossenen/vollen Räumen Eintragstokens und Positionsfelder fehlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"waiting_room_required":true,"status":"waiting","mode":"booking","offer":"offer_012345abcdef_123456","scope":"account","queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","position":2,"estimated_wait_seconds":60,"active_limit":100,"free_places":12,"expires_at":1792400090}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"waiting_room_required":{"type":"boolean"},"status":{"type":"string"},"mode":{"type":"string","enum":["booking","waitlist"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"scope":{"type":"string","enum":["account","offer"]},"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"position":{"type":"integer"},"estimated_wait_seconds":{"type":"integer"},"active_limit":{"type":"integer"},"free_places":{"type":["integer","null"]},"expires_at":{"type":"integer"},"admission_token":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"mode":{"description":"Nur exakt waitlist wählt die Warteliste; alle anderen Werte oder fehlender Wert wählen booking.","examples":["booking","waitlist"],"x-edoobox-canonicalType":"string"},"session":{"x-edoobox-canonicalType":"string","description":"Optionaler Sitzungsbezeichner; wird als string gehasht. Ohne Wert werden Anmeldung und PHP-Session verwendet."}},"additionalProperties":true,"required":["offer"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Buchungs-Warteraum betreten","value":{"data":{"offer":"offer_012345abcdef_123456","mode":"booking","session":"browser-session-demo"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingqueue.php","method":"postBookingqueue","line":11,"branch":{"file":"application/models/v2/Bookingqueue.php","line":21}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bookingqueue","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/bookingqueue/list/release Warteraum-Platz freigeben Gibt den Eintrag über queue_token oder admission_token frei und lässt gegebenenfalls wartende Einträge nachrücken. Nur completed=true als echtes JSON-Boolean setzt completed; sonst cancelled. DELETE auf die Basisroute braucht zusätzlich ein oberstes id-Feld für den Dispatcher; der Platz wird anhand des Tokens bestimmt. - Operation-ID: post_v2_bookingqueue_list_release - Dokumentation: /docs/api/v2/operations/post-v2-bookingqueue-list-release-8784b47f5351/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "completed": false } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/bookingqueue/list/release' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bookingqueue/list/release"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "completed": false } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bookingqueue/list/release" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"queue_token\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"completed\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_bookingqueue_list_release","summary":"Warteraum-Platz freigeben","description":"Gibt den Eintrag über queue_token oder admission_token frei und lässt gegebenenfalls wartende Einträge nachrücken. Nur completed=true als echtes JSON-Boolean setzt completed; sonst cancelled. DELETE auf die Basisroute braucht zusätzlich ein oberstes id-Feld für den Dispatcher; der Platz wird anhand des Tokens bestimmt.","tags":["bookingqueue"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"released":true,"status":"cancelled","mode":"booking","offer":"offer_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"released":{"type":"boolean"},"status":{"type":"string","enum":["completed","cancelled"]},"mode":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"admission_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"completed":{"x-edoobox-canonicalType":"boolean","description":"Nur echtes JSON true setzt completed; andere Werte setzen cancelled."}},"additionalProperties":true,"anyOf":[{"required":["queue_token"]},{"required":["admission_token"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Warteraum-Platz freigeben","value":{"data":{"queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","completed":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingqueue.php","method":"postBookingqueue","line":11,"branch":{"file":"application/models/v2/Bookingqueue.php","line":17}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bookingqueue","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/bookingqueue/list/status Status eines Warteraum-Eintrags prüfen Liest Status, Position, erwartete Wartezeit, freie Plätze und Ablaufzeit des Eintrags mit queue_token. Der Aufruf bereinigt zuvor abgelaufene Einträge und kann freie Plätze an Wartende vergeben; er ist deshalb trotz Statusabfrage zustandsändernd. GET übermittelt data[queue_token] als PHP-Bracket-Query, POST verwendet data.queue_token. - Operation-ID: post_v2_bookingqueue_list_status - Dokumentation: /docs/api/v2/operations/post-v2-bookingqueue-list-status-24ee3fc45555/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/bookingqueue/list/status' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bookingqueue/list/status"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "queue_token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bookingqueue/list/status" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"queue_token\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_bookingqueue_list_status","summary":"Status eines Warteraum-Eintrags prüfen","description":"Liest Status, Position, erwartete Wartezeit, freie Plätze und Ablaufzeit des Eintrags mit queue_token. Der Aufruf bereinigt zuvor abgelaufene Einträge und kann freie Plätze an Wartende vergeben; er ist deshalb trotz Statusabfrage zustandsändernd. GET übermittelt data[queue_token] als PHP-Bracket-Query, POST verwendet data.queue_token.","tags":["bookingqueue"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält den Wartestatus; bei join können bei geschlossenen/vollen Räumen Eintragstokens und Positionsfelder fehlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"waiting_room_required":true,"status":"waiting","mode":"booking","offer":"offer_012345abcdef_123456","scope":"account","queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","position":2,"estimated_wait_seconds":60,"active_limit":100,"free_places":12,"expires_at":1792400090}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"waiting_room_required":{"type":"boolean"},"status":{"type":"string"},"mode":{"type":"string","enum":["booking","waitlist"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},"scope":{"type":"string","enum":["account","offer"]},"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"},"position":{"type":"integer"},"estimated_wait_seconds":{"type":"integer"},"active_limit":{"type":"integer"},"free_places":{"type":["integer","null"]},"expires_at":{"type":"integer"},"admission_token":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"queue_token":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true,"required":["queue_token"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Status eines Warteraum-Eintrags prüfen","value":{"data":{"queue_token":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bookingqueue.php","method":"postBookingqueue","line":11,"branch":{"file":"application/models/v2/Bookingqueue.php","line":19}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bookingqueue","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/bootstrap Bootstrap-Konfiguration auflisten Liest gespeicherte Bootstrap-Versionen, Variablen und Kompilierungszustände für die Gestaltung der Teilnehmeransicht. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_bootstrap - Dokumentation: /docs/api/v2/operations/get-v2-bootstrap-972e7b8cf2de/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/bootstrap' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_bootstrap","summary":"Bootstrap-Konfiguration auflisten","description":"Liest gespeicherte Bootstrap-Versionen, Variablen und Kompilierungszustände für die Gestaltung der Teilnehmeransicht. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"bootstrap_012345abcdef_123456":{"id":"bootstrap_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","examples":["bootstrap_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"type":"string"},"version":{"enum":[3.7,5.3,false]},"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_use_wp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"bs_compile_recommended":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":137,"lineStart":137,"lineEnd":155,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"a4babb1fa430cb91d3df6f40352b94e4166c4c050057ae93fce02b6b53c38eab","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile_running":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":156,"lineStart":156,"lineEnd":165,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"5f0a092f073f08da99a0e84d2c16a33b87da640fb2774b85a5915a18acb95c2b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"last_change":{"type":"string","format":"date-time","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"theme_mode":{"type":"string","enum":["light","dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"expert_mode":{"type":"string","enum":["simple","expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","examples":["bootstrap_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"type":"string"},"version":{"enum":[3.7,5.3,false]},"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_use_wp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"bs_compile_recommended":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":137,"lineStart":137,"lineEnd":155,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"a4babb1fa430cb91d3df6f40352b94e4166c4c050057ae93fce02b6b53c38eab","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile_running":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":156,"lineStart":156,"lineEnd":165,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"5f0a092f073f08da99a0e84d2c16a33b87da640fb2774b85a5915a18acb95c2b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"last_change":{"type":"string","format":"date-time","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"theme_mode":{"type":"string","enum":["light","dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"expert_mode":{"type":"string","enum":["simple","expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"getBootstrap","line":275},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/bootstrap Bootstrap-Konfiguration anlegen Erstellt eine benannte Bootstrap-Konfiguration für ein Buchungsdesign. name, version und bs_variables sind erforderlich. Die Speicherung kompiliert noch kein CSS; dafür anschließend compiling_scss aufrufen. Variablen sind JSON-Werte; beim Kompilieren werden nur bekannte SCSS-Variablen verwendet. data akzeptiert einen Datensatz oder ein Array mit bis zu 100 Datensätzen; bei einem Batch enthält data.id ein Array öffentlicher IDs. - Operation-ID: post_v2_bootstrap - Dokumentation: /docs/api/v2/operations/post-v2-bootstrap-639f33224b60/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Sommerfarben", "version": "5.3", "bs_variables": { "primary": "#336699" }, "bs_variables_dark": { "primary": "#99bbdd" }, "theme_mode": "light" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/bootstrap' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Sommerfarben", "version": "5.3", "bs_variables": { "primary": "#336699" }, "bs_variables_dark": { "primary": "#99bbdd" }, "theme_mode": "light" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Sommerfarben\",\"version\":\"5.3\",\"bs_variables\":{\"primary\":\"#336699\"},\"bs_variables_dark\":{\"primary\":\"#99bbdd\"},\"theme_mode\":\"light\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_bootstrap","summary":"Bootstrap-Konfiguration anlegen","description":"Erstellt eine benannte Bootstrap-Konfiguration für ein Buchungsdesign. name, version und bs_variables sind erforderlich. Die Speicherung kompiliert noch kein CSS; dafür anschließend compiling_scss aufrufen. Variablen sind JSON-Werte; beim Kompilieren werden nur bekannte SCSS-Variablen verwendet. data akzeptiert einen Datensatz oder ein Array mit bis zu 100 Datensätzen; bei einem Batch enthält data.id ein Array öffentlicher IDs.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert, öffentliche id und bei Uploads signierte upload_url; öffentliche Dateien liefern zusätzlich cdn_url. Bei fehlgeschlagenem Transaktionsabschluss enthält data insert=false und id=null; error ist ED80100.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"bootstrap_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"oneOf":[{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["bootstrap_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["bootstrap_012345abcdef_123456"]}},{"type":"null"}],"description":"Bei erfolgreichem Einzelinsert öffentliche v2-ID, bei erfolgreichem Batch ein Array öffentlicher IDs; bei fehlgeschlagenem Transaktionsabschluss null zusammen mit insert=false und error=ED80100."}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"file":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":32},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":50,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":46},"version":{"x-edoobox-knownInputs":["3.7","5.3"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["3.7"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":59},"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.\n\nEncode whole supplied JSON value; database null becomes [] on output, otherwise decode stored JSON to original JSON type.\n\nScope / limitations: Source proves JSON storage, not map or allowed-SCSS-variable restrictions. Compile action has separate consumption.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"examples":[{"primary":"#336699"},[]],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.\n\nEncode whole supplied JSON value; database null becomes [] on output, otherwise decode stored JSON to original JSON type.\n\nScope / limitations: Source proves JSON storage, not map or allowed-SCSS-variable restrictions. Compile action has separate consumption.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"examples":[{"primary":"#336699"},[]],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"bs_compile":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"bs_use_wp":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":128,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"bs_compile_recommended":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":137,"lineStart":137,"lineEnd":155,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"a4babb1fa430cb91d3df6f40352b94e4166c4c050057ae93fce02b6b53c38eab","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"bs_compile_running":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":156,"lineStart":156,"lineEnd":165,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"5f0a092f073f08da99a0e84d2c16a33b87da640fb2774b85a5915a18acb95c2b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"last_change":{"readOnly":true,"description":"Getter returns time() without reading client input. No insert_default/update_default is declared, so omission does not invoke it. Output uses date(c,strtotime(storedValue)), not dQ. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"theme_mode":{"x-edoobox-canonicalType":"string","description":"Truthy input stored without enum check; falsy input defaults to light. Output recognizes only listed values, falling back to light.\n\nInput value ?: light; output in_array recognized mode else light.\n\nScope / limitations: Do not impose request enum based on recognized output values.","x-edoobox-recognizedValues":["light","dark"],"x-edoobox-schemaStatus":"source-reviewed","examples":["dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"expert_mode":{"x-edoobox-canonicalType":"string","description":"Truthy input stored without enum check; falsy input defaults to simple. Output recognizes only listed values, falling back to simple.\n\nInput value ?: simple; output in_array recognized mode else simple.\n\nScope / limitations: Do not impose request enum based on recognized output values.","x-edoobox-recognizedValues":["simple","expert"],"x-edoobox-schemaStatus":"source-reviewed","examples":["expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","version","bs_variables"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"file":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":32},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":50,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":46},"version":{"x-edoobox-knownInputs":["3.7","5.3"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["3.7"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":59},"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.\n\nEncode whole supplied JSON value; database null becomes [] on output, otherwise decode stored JSON to original JSON type.\n\nScope / limitations: Source proves JSON storage, not map or allowed-SCSS-variable restrictions. Compile action has separate consumption.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"examples":[{"primary":"#336699"},[]],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.\n\nEncode whole supplied JSON value; database null becomes [] on output, otherwise decode stored JSON to original JSON type.\n\nScope / limitations: Source proves JSON storage, not map or allowed-SCSS-variable restrictions. Compile action has separate consumption.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"examples":[{"primary":"#336699"},[]],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"bs_compile":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"bs_use_wp":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":128,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"bs_compile_recommended":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":137,"lineStart":137,"lineEnd":155,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"a4babb1fa430cb91d3df6f40352b94e4166c4c050057ae93fce02b6b53c38eab","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"bs_compile_running":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":156,"lineStart":156,"lineEnd":165,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"5f0a092f073f08da99a0e84d2c16a33b87da640fb2774b85a5915a18acb95c2b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"last_change":{"readOnly":true,"description":"Getter returns time() without reading client input. No insert_default/update_default is declared, so omission does not invoke it. Output uses date(c,strtotime(storedValue)), not dQ. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"theme_mode":{"x-edoobox-canonicalType":"string","description":"Truthy input stored without enum check; falsy input defaults to light. Output recognizes only listed values, falling back to light.\n\nInput value ?: light; output in_array recognized mode else light.\n\nScope / limitations: Do not impose request enum based on recognized output values.","x-edoobox-recognizedValues":["light","dark"],"x-edoobox-schemaStatus":"source-reviewed","examples":["dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"expert_mode":{"x-edoobox-canonicalType":"string","description":"Truthy input stored without enum check; falsy input defaults to simple. Output recognizes only listed values, falling back to simple.\n\nInput value ?: simple; output in_array recognized mode else simple.\n\nScope / limitations: Do not impose request enum based on recognized output values.","x-edoobox-recognizedValues":["simple","expert"],"x-edoobox-schemaStatus":"source-reviewed","examples":["expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","version","bs_variables"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Bootstrap-Konfiguration anlegen","value":{"data":{"name":"Sommerfarben","version":"5.3","bs_variables":{"primary":"#336699"},"bs_variables_dark":{"primary":"#99bbdd"},"theme_mode":"light"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"postBootstrap","line":213},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/bootstrap Bootstrap-Konfiguration ändern Ändert Name, Variablen und Darstellungsoptionen einer Bootstrap-Konfiguration. version und file bleiben unveränderlich. Variablenänderungen benötigen anschließend eine separate SCSS-Kompilierung. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_bootstrap - Dokumentation: /docs/api/v2/operations/put-v2-bootstrap-f47edac9bbfd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Sommerdesign" }, "id": "bootstrap_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/bootstrap' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Sommerdesign" }, "id": "bootstrap_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Sommerdesign\"},\"id\":\"bootstrap_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_bootstrap","summary":"Bootstrap-Konfiguration ändern","description":"Ändert Name, Variablen und Darstellungsoptionen einer Bootstrap-Konfiguration. version und file bleiben unveränderlich. Variablenänderungen benötigen anschließend eine separate SCSS-Kompilierung. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"bootstrap_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["bootstrap_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"file":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":50,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":46},"version":false,"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.\n\nEncode whole supplied JSON value; database null becomes [] on output, otherwise decode stored JSON to original JSON type.\n\nScope / limitations: Source proves JSON storage, not map or allowed-SCSS-variable restrictions. Compile action has separate consumption.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"examples":[{"primary":"#336699"},[]],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.\n\nEncode whole supplied JSON value; database null becomes [] on output, otherwise decode stored JSON to original JSON type.\n\nScope / limitations: Source proves JSON storage, not map or allowed-SCSS-variable restrictions. Compile action has separate consumption.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"examples":[{"primary":"#336699"},[]],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_use_wp":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":128,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"bs_compile_recommended":false,"bs_compile_running":false,"last_change":{"readOnly":true,"description":"Getter returns time() without reading client input. No insert_default/update_default is declared, so omission does not invoke it. Output uses date(c,strtotime(storedValue)), not dQ. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"theme_mode":{"x-edoobox-canonicalType":"string","description":"Truthy input stored without enum check; falsy input defaults to light. Output recognizes only listed values, falling back to light.\n\nInput value ?: light; output in_array recognized mode else light.\n\nScope / limitations: Do not impose request enum based on recognized output values.","x-edoobox-recognizedValues":["light","dark"],"x-edoobox-schemaStatus":"source-reviewed","examples":["dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"expert_mode":{"x-edoobox-canonicalType":"string","description":"Truthy input stored without enum check; falsy input defaults to simple. Output recognizes only listed values, falling back to simple.\n\nInput value ?: simple; output in_array recognized mode else simple.\n\nScope / limitations: Do not impose request enum based on recognized output values.","x-edoobox-recognizedValues":["simple","expert"],"x-edoobox-schemaStatus":"source-reviewed","examples":["expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["file","version","bs_compile_recommended","bs_compile_running"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["bootstrap_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Bootstrap-Konfiguration ändern","value":{"data":{"name":"Sommerdesign"},"id":"bootstrap_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"updateBootstrap","line":230},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/bootstrap/{id} Bootstrap-Konfiguration lesen Liest gespeicherte Bootstrap-Versionen, Variablen und Kompilierungszustände für die Gestaltung der Teilnehmeransicht. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_bootstrap_id - Dokumentation: /docs/api/v2/operations/get-v2-bootstrap-id-0b44550ed9ec/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/bootstrap/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_bootstrap_id","summary":"Bootstrap-Konfiguration lesen","description":"Liest gespeicherte Bootstrap-Versionen, Variablen und Kompilierungszustände für die Gestaltung der Teilnehmeransicht. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"bootstrap_012345abcdef_123456":{"id":"bootstrap_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","examples":["bootstrap_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"type":"string"},"version":{"enum":[3.7,5.3,false]},"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_use_wp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"bs_compile_recommended":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":137,"lineStart":137,"lineEnd":155,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"a4babb1fa430cb91d3df6f40352b94e4166c4c050057ae93fce02b6b53c38eab","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile_running":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":156,"lineStart":156,"lineEnd":165,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"5f0a092f073f08da99a0e84d2c16a33b87da640fb2774b85a5915a18acb95c2b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"last_change":{"type":"string","format":"date-time","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"theme_mode":{"type":"string","enum":["light","dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"expert_mode":{"type":"string","enum":["simple","expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","examples":["bootstrap_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"type":"string"},"version":{"enum":[3.7,5.3,false]},"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_use_wp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"bs_compile_recommended":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":137,"lineStart":137,"lineEnd":155,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"a4babb1fa430cb91d3df6f40352b94e4166c4c050057ae93fce02b6b53c38eab","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile_running":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":156,"lineStart":156,"lineEnd":165,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"5f0a092f073f08da99a0e84d2c16a33b87da640fb2774b85a5915a18acb95c2b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"last_change":{"type":"string","format":"date-time","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"theme_mode":{"type":"string","enum":["light","dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"expert_mode":{"type":"string","enum":["simple","expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"getBootstrap","line":275},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/bootstrap/{id} Bootstrap-Konfiguration ändern Ändert Name, Variablen und Darstellungsoptionen einer Bootstrap-Konfiguration. version und file bleiben unveränderlich. Variablenänderungen benötigen anschließend eine separate SCSS-Kompilierung. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_bootstrap_id - Dokumentation: /docs/api/v2/operations/put-v2-bootstrap-id-fe697f659f86/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Sommerdesign" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/bootstrap/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Sommerdesign" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Sommerdesign\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_bootstrap_id","summary":"Bootstrap-Konfiguration ändern","description":"Ändert Name, Variablen und Darstellungsoptionen einer Bootstrap-Konfiguration. version und file bleiben unveränderlich. Variablenänderungen benötigen anschließend eine separate SCSS-Kompilierung. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"bootstrap_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["bootstrap_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"file":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":50,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":46},"version":false,"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.\n\nEncode whole supplied JSON value; database null becomes [] on output, otherwise decode stored JSON to original JSON type.\n\nScope / limitations: Source proves JSON storage, not map or allowed-SCSS-variable restrictions. Compile action has separate consumption.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"examples":[{"primary":"#336699"},[]],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.\n\nEncode whole supplied JSON value; database null becomes [] on output, otherwise decode stored JSON to original JSON type.\n\nScope / limitations: Source proves JSON storage, not map or allowed-SCSS-variable restrictions. Compile action has separate consumption.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"examples":[{"primary":"#336699"},[]],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_use_wp":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":128,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"bs_compile_recommended":false,"bs_compile_running":false,"last_change":{"readOnly":true,"description":"Getter returns time() without reading client input. No insert_default/update_default is declared, so omission does not invoke it. Output uses date(c,strtotime(storedValue)), not dQ. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"theme_mode":{"x-edoobox-canonicalType":"string","description":"Truthy input stored without enum check; falsy input defaults to light. Output recognizes only listed values, falling back to light.\n\nInput value ?: light; output in_array recognized mode else light.\n\nScope / limitations: Do not impose request enum based on recognized output values.","x-edoobox-recognizedValues":["light","dark"],"x-edoobox-schemaStatus":"source-reviewed","examples":["dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"expert_mode":{"x-edoobox-canonicalType":"string","description":"Truthy input stored without enum check; falsy input defaults to simple. Output recognizes only listed values, falling back to simple.\n\nInput value ?: simple; output in_array recognized mode else simple.\n\nScope / limitations: Do not impose request enum based on recognized output values.","x-edoobox-recognizedValues":["simple","expert"],"x-edoobox-schemaStatus":"source-reviewed","examples":["expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["file","version","bs_compile_recommended","bs_compile_running"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Bootstrap-Konfiguration ändern","value":{"data":{"name":"Sommerdesign"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"updateBootstrap","line":230},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/bootstrap/{id}/compiling_scss Bootstrap-5.3-CSS kompilieren Startet die Kompilierung der gespeicherten hellen und dunklen SCSS-Variablen einer eigenen Bootstrap-Konfiguration ab Version 5.3. Die Antwort bestätigt den Start mit compile_run=true; sie bestätigt noch keinen erfolgreichen Build. Der Handler schreibt CSS, aktualisiert Buildstatus und Dateireferenz und meldet den Abschluss per Websocket. Ein unveränderter, bereits vorhandener Build ergibt no_change. Ein Body ist nicht erforderlich; zuerst die Variablen mit PUT speichern. - Operation-ID: put_v2_bootstrap_id_compiling_scss - Dokumentation: /docs/api/v2/operations/put-v2-bootstrap-id-compiling-scss-3166b34ecf96/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/bootstrap/'"${PATH_ID}"'/compiling_scss' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap/{id}/compiling_scss"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap/{id}/compiling_scss" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_bootstrap_id_compiling_scss","summary":"Bootstrap-5.3-CSS kompilieren","description":"Startet die Kompilierung der gespeicherten hellen und dunklen SCSS-Variablen einer eigenen Bootstrap-Konfiguration ab Version 5.3. Die Antwort bestätigt den Start mit compile_run=true; sie bestätigt noch keinen erfolgreichen Build. Der Handler schreibt CSS, aktualisiert Buildstatus und Dateireferenz und meldet den Abschluss per Websocket. Ein unveränderter, bereits vorhandener Build ergibt no_change. Ein Body ist nicht erforderlich; zuerst die Variablen mit PUT speichern.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bestätigung des gestarteten Builds in data; endgültigen Kompilierungsstatus anschließend lesen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"bootstrap_012345abcdef_123456","update":true,"compile_run":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["bootstrap_012345abcdef_123456"]},"update":{"type":"boolean"},"compile_run":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Bootstrap-5.3-CSS kompilieren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"updateBootstrap","line":230,"branch":{"file":"application/models/v2/Bootstrap.php","line":237}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/bootstrap/{id}/wp-css WordPress-CSS erzeugen und CDN-Adresse beziehen Liefert die CDN-Adresse der unter .edoobox-plugin gekapselten WordPress-CSS-Ausgabe. Bei Bootstrap 3.7 kompiliert der Handler die Ausgangs-CSS-Datei und schreibt die WordPress-Ausgabe in Dateisystem oder Cloud-Speicher. Bei Bootstrap 5.3 berechnet dieser direkte GET nur die Adresse; die passende Datei muss durch die vorherige SCSS-Kompilierung mit bs_use_wp erzeugt worden sein. Der Selektor id ist der gespeicherte Bootstrap-Dateiname aus bootstrap.file, statt einer öffentlichen Bootstrap-Objekt-ID. Dieser GET kann Speicherdateien ändern und darf nicht als reine Leseoperation behandelt werden. - Operation-ID: get_v2_bootstrap_id_wp_css - Dokumentation: /docs/api/v2/operations/get-v2-bootstrap-id-wp-css-e5a0dfae3859/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Bootstrap-Dateiname aus bootstrap.file; keine Datenbank-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Bootstrap-Dateiname aus bootstrap.file; keine Datenbank-ID.","schema":{"type":"string","examples":["bs5.3_synthetic_bootstrap_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/bootstrap/'"${PATH_ID}"'/wp-css' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap/{id}/wp-css"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap/{id}/wp-css" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_bootstrap_id_wp_css","summary":"WordPress-CSS erzeugen und CDN-Adresse beziehen","description":"Liefert die CDN-Adresse der unter .edoobox-plugin gekapselten WordPress-CSS-Ausgabe. Bei Bootstrap 3.7 kompiliert der Handler die Ausgangs-CSS-Datei und schreibt die WordPress-Ausgabe in Dateisystem oder Cloud-Speicher. Bei Bootstrap 5.3 berechnet dieser direkte GET nur die Adresse; die passende Datei muss durch die vorherige SCSS-Kompilierung mit bs_use_wp erzeugt worden sein. Der Selektor id ist der gespeicherte Bootstrap-Dateiname aus bootstrap.file, statt einer öffentlichen Bootstrap-Objekt-ID. Dieser GET kann Speicherdateien ändern und darf nicht als reine Leseoperation behandelt werden.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Bootstrap-Dateiname aus bootstrap.file; keine Datenbank-ID.","schema":{"type":"string","examples":["bs5.3_synthetic_bootstrap_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.cdn_url verweist auf die erzeugte WordPress-CSS-Datei.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"cdn_url":"https://cdn.example.invalid/wp-css_synthetic.css"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"cdn_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["cdn_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Bootstrap.php","line":281},"reason":"GET compiles Bootstrap assets."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"getBootstrap","line":275,"branch":{"file":"application/models/v2/Bootstrap.php","line":281}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/bootstrap/list Bootstrap-Konfiguration auflisten Liest gespeicherte Bootstrap-Versionen, Variablen und Kompilierungszustände für die Gestaltung der Teilnehmeransicht. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_bootstrap_list - Dokumentation: /docs/api/v2/operations/get-v2-bootstrap-list-e9a114b8607f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/bootstrap/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_bootstrap_list","summary":"Bootstrap-Konfiguration auflisten","description":"Liest gespeicherte Bootstrap-Versionen, Variablen und Kompilierungszustände für die Gestaltung der Teilnehmeransicht. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"bootstrap_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"bootstrap_012345abcdef_123456":{"id":"bootstrap_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","examples":["bootstrap_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"type":"string"},"version":{"enum":[3.7,5.3,false]},"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_use_wp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"bs_compile_recommended":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":137,"lineStart":137,"lineEnd":155,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"a4babb1fa430cb91d3df6f40352b94e4166c4c050057ae93fce02b6b53c38eab","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile_running":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":156,"lineStart":156,"lineEnd":165,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"5f0a092f073f08da99a0e84d2c16a33b87da640fb2774b85a5915a18acb95c2b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"last_change":{"type":"string","format":"date-time","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"theme_mode":{"type":"string","enum":["light","dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"expert_mode":{"type":"string","enum":["simple","expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","examples":["bootstrap_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":32},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"type":"string"},"version":{"enum":[3.7,5.3,false]},"bs_variables":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":87,"lineStart":87,"lineEnd":102,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"1701d9b7a44e1107fcd288291af07144836a46ca9a6e65b32b9c09b6889a8a29","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_variables_dark":{"description":"Any JSON value is json_encode stored and json_decode returned; no shape or entry validator.","x-edoobox-schemaStatus":"reviewed-any-json","x-edoobox-supportedWireShapes":["object","array","string","number","boolean","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":103,"lineStart":103,"lineEnd":118,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"e6aea7d29666887d9d089544e15d24124bfbbaaa555c90e2951c01ee2410d5d0","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":119,"lineStart":119,"lineEnd":127,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"29d6f03b0256fe79df9446b112cb13febeb1ff6dfdd215d71564e60fc975e614","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_use_wp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"bs_compile_recommended":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":137,"lineStart":137,"lineEnd":155,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"a4babb1fa430cb91d3df6f40352b94e4166c4c050057ae93fce02b6b53c38eab","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"bs_compile_running":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":156,"lineStart":156,"lineEnd":165,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"5f0a092f073f08da99a0e84d2c16a33b87da640fb2774b85a5915a18acb95c2b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"last_change":{"type":"string","format":"date-time","x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":166,"lineStart":166,"lineEnd":174,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"75e61eb9912d5e5d807513162f5e8e7b913bfb7df0117fd8e9c0d22918378357","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"theme_mode":{"type":"string","enum":["light","dark"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":175,"lineStart":175,"lineEnd":187,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"c26a3640873dbb970a86a34dc377ab7668c89352d62fb19268c48bea544cc1fb","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"expert_mode":{"type":"string","enum":["simple","expert"],"x-edoobox-contractSource":[{"file":"application/models/v2/Bootstrap.php","line":188,"lineStart":188,"lineEnd":200,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"9c8f7963bf068fc25e03b471f35dcb5075b192a8e185d64afa27239e49cbf6a1","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Bootstrap.php","line":213,"lineStart":213,"lineEnd":244,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","spanSha256":"f4438476c2439b12f496bf485284ce3f980679fa045059af724d73f10ffa52c5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"getBootstrap","line":275},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/bootstrap/list/cdn Design und CSS für ein Angebot oder eine Kategorie bestimmen Bestimmt das wirksame Buchungsdesign einschließlich geerbter Kategorie- und Standardzuordnung. Erforderlich ist ein filter-Eintrag offer oder category mit öffentlicher v2-ID; offer hat Vorrang. Liefert design, bootstrap und die CSS-Adresse cdn_url, ohne CSS zu kompilieren. - Operation-ID: get_v2_bootstrap_list_cdn - Dokumentation: /docs/api/v2/operations/get-v2-bootstrap-list-cdn-b00ad3a64e3a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Genau den gewünschten offer- oder category-Selektor mit öffentlicher ID angeben. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Genau den gewünschten offer- oder category-Selektor mit öffentlicher ID angeben.","content":{"application/json":{"schema":{"type":"array","minItems":1,"items":{"type":"object","properties":{"property":{"type":"string","enum":["offer","category"]},"value":{"type":"string"}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"offer","value":"offer_012345abcdef_123456"}]}}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/bootstrap/list/cdn' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/bootstrap/list/cdn"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/bootstrap/list/cdn" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_bootstrap_list_cdn","summary":"Design und CSS für ein Angebot oder eine Kategorie bestimmen","description":"Bestimmt das wirksame Buchungsdesign einschließlich geerbter Kategorie- und Standardzuordnung. Erforderlich ist ein filter-Eintrag offer oder category mit öffentlicher v2-ID; offer hat Vorrang. Liefert design, bootstrap und die CSS-Adresse cdn_url, ohne CSS zu kompilieren.","tags":["bootstrap"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Genau den gewünschten offer- oder category-Selektor mit öffentlicher ID angeben.","content":{"application/json":{"schema":{"type":"array","minItems":1,"items":{"type":"object","properties":{"property":{"type":"string","enum":["offer","category"]},"value":{"type":"string"}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"offer","value":"offer_012345abcdef_123456"}]}}}],"responses":{"200":{"description":"data enthält bootstrap, design und cdn_url.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"bootstrap":"bootstrap_012345abcdef_123456","design":"design_012345abcdef_123456","cdn_url":"https://cdn.example.invalid/synthetic-bootstrap.css"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"bootstrap":{"type":"string","pattern":"^bootstrap_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["bootstrap_012345abcdef_123456"]},"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]},"cdn_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["bootstrap","design","cdn_url"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/18","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Bootstrap.php","line":275,"lineEnd":290,"sha256":"336883443f0523fd196dc70b746a5323dbc9b48c5f0f7e4b13feb68b32a1930d","reason":"cdn selects getDesginData; wp-css compilation is a separate branch."},"reason":"Returns CDN/style URLs for the selected existing design.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/18","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Bootstrap.php","method":"getBootstrap","line":275,"branch":{"file":"application/models/v2/Bootstrap.php","line":288}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"bootstrap","short":"boot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/cancellationcondition Stornierungsgruppe entfernen Markiert Gruppe und Regeln als gelöscht und löst Angebotszuordnungen. Die aktuelle Standardgruppe kann nicht gelöscht werden. Ohne Pfad-ID ist id im Query oder JSON-Body erforderlich. - Operation-ID: delete_v2_cancellationcondition - Dokumentation: /docs/api/v2/operations/delete-v2-cancellationcondition-c6a1f355d04e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Erforderlich im Query oder top-level im JSON-Body. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Erforderlich im Query oder top-level im JSON-Body.","example":"cancellationconditiongroup_012345abcdef_123456"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "cancellationconditiongroup_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/cancellationcondition' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/cancellationcondition"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "cancellationconditiongroup_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/cancellationcondition" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"cancellationconditiongroup_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_cancellationcondition","summary":"Stornierungsgruppe entfernen","description":"Markiert Gruppe und Regeln als gelöscht und löst Angebotszuordnungen. Die aktuelle Standardgruppe kann nicht gelöscht werden. Ohne Pfad-ID ist id im Query oder JSON-Body erforderlich.","tags":["cancellationcondition"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Erforderlich im Query oder top-level im JSON-Body.","example":"cancellationconditiongroup_012345abcdef_123456"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"cancellationconditiongroup_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"Stornierungsgruppe entfernen","value":{"id":"cancellationconditiongroup_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Cancellationcondition.php","method":"deleteCancellationcondition","line":139},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"cancellationcondition","short":"caco","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/cancellationcondition Stornierungsgruppen und Widerrufsregeln abrufen Liest eine Gruppe per Pfad-ID bzw. id-Filter oder eine nach Gruppen-ID indizierte Map. Vor dem Lesen werden fehlende Standardgruppen angelegt und der Standardstatus sichergestellt. - Operation-ID: get_v2_cancellationcondition - Dokumentation: /docs/api/v2/operations/get-v2-cancellationcondition-5edbe705ded3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"cancellationconditiongroup_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/cancellationcondition' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/cancellationcondition"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/cancellationcondition" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_cancellationcondition","summary":"Stornierungsgruppen und Widerrufsregeln abrufen","description":"Liest eine Gruppe per Pfad-ID bzw. id-Filter oder eine nach Gruppen-ID indizierte Map. Vor dem Lesen werden fehlende Standardgruppen angelegt und der Standardstatus sichergestellt.","tags":["cancellationcondition"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"cancellationconditiongroup_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Gruppenobjekt beim gültigen id-Selektor, sonst ID-Map; leere PHP-Map wird als [] ausgegeben.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"cancellationconditiongroup_012345abcdef_123456":{"id":"cancellationconditiongroup_012345abcdef_123456","name":"Flexible Stornierung","name_translations":{"de":"Flexible Stornierung"},"slug":"flexible-stornierung","active":true,"default":false,"auto_refund":true,"refund_method":"online","legaltext":false,"vat":false,"withdrawal":{"enabled":true,"url":"https://app2.edoobox.com/EXAMPLE/?edwithdrawal=cancellationconditiongroup_012345abcdef_123456","checkbox_label":"Beispiel einer Widerrufsbestätigung","checkbox_label_translations":{"de":"Beispiel einer Widerrufsbestätigung"}},"rules":[{"id":"cancellationconditionrule_012345abcdef_123456","days_before_start":14,"action":"automatic","fee_percent":25,"active":true,"sort_order":0}],"compact_table_html":"
14 Tage25 %
"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Cancellationcondition.php","line":190},"reason":"GET ensures default groups/configuration and can INSERT missing rows (Dunning.php206)."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Cancellationcondition.php","method":"getCancellationcondition","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"cancellationcondition","short":"caco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/cancellationcondition Stornierungsgruppe und Gebührenregeln anlegen Erstellt eine Gruppe samt Regeln. name oder name_translations muss mindestens einen nichtleeren Text enthalten. default=true aktiviert die Gruppe und löst die bisherige Standardgruppe ab. Fehlende Standardgruppen werden vorher angelegt. - Operation-ID: post_v2_cancellationcondition - Dokumentation: /docs/api/v2/operations/post-v2-cancellationcondition-454977a43840/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Flexible Stornierung", "active": true, "rules": [ { "days_before_start": 14, "action": "automatic", "fee_percent": 25 } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/cancellationcondition' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/cancellationcondition"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Flexible Stornierung", "active": true, "rules": [ { "days_before_start": 14, "action": "automatic", "fee_percent": 25 } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/cancellationcondition" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Flexible Stornierung\",\"active\":true,\"rules\":[{\"days_before_start\":14,\"action\":\"automatic\",\"fee_percent\":25}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_cancellationcondition","summary":"Stornierungsgruppe und Gebührenregeln anlegen","description":"Erstellt eine Gruppe samt Regeln. name oder name_translations muss mindestens einen nichtleeren Text enthalten. default=true aktiviert die Gruppe und löst die bisherige Standardgruppe ab. Fehlende Standardgruppen werden vorher angelegt.","tags":["cancellationcondition"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"cancellationconditiongroup_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"name_translations":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"active":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"status":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"default":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"auto_refund":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"refund_method":{"type":"string","description":"online, voucher oder balance; unbekannte Werte werden online."},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"enum":[false,null,"",0,"0"]}],"description":"Öffentliche v2-ID; false, null, leere Zeichenkette, 0 und \"0\" entfernen die Zuordnung."},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"enum":[false,null,"",0,"0"]}],"description":"Öffentliche v2-ID; false, null, leere Zeichenkette, 0 und \"0\" entfernen die Zuordnung."},"withdrawal":{"type":"object","properties":{"enabled":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"checkbox_label":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"checkbox_label_translations":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]}},"additionalProperties":true},"withdrawal_enabled":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"withdrawal_checkbox_label":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."},"action":{"type":"string","description":"automatic oder admin_task; sonst admin_task."},"action_type":{"type":"string","description":"Alias für action."},"fee_percent":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."},"active":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"status":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"sort_order":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."}},"additionalProperties":true,"description":"Negative Tage werden 0; Prozentwerte werden auf 0..100 begrenzt. id aktualisiert nur eine bestehende Regel dieser Gruppe."}}},"additionalProperties":true,"anyOf":[{"required":["name"]},{"required":["name_translations"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Stornierungsgruppe und Gebührenregeln anlegen","value":{"data":{"name":"Flexible Stornierung","active":true,"rules":[{"days_before_start":14,"action":"automatic","fee_percent":25}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Cancellationcondition.php","method":"postCancellationcondition","line":49},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"cancellationcondition","short":"caco","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/cancellationcondition Stornierungsgruppe und Regelwerk ändern Ändert eine vorhandene Gruppe; data darf nichtleer sein. Ein übergebenes rules-Array ersetzt das vollständige Regelwerk; [] löscht alle Regeln. Passende Regel-IDs aktualisieren bestehende Gruppenregeln, sonst werden neue Regeln angelegt. default=true aktiviert die Gruppe und setzt den bisherigen Standard zurück. - Operation-ID: put_v2_cancellationcondition - Dokumentation: /docs/api/v2/operations/put-v2-cancellationcondition-9a8c8f1050e6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name_translations": { "de": "Flexible Bedingungen" }, "rules": [ { "days_before_start": 7, "action": "admin_task", "fee_percent": 50 } ] }, "id": "cancellationconditiongroup_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/cancellationcondition' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/cancellationcondition"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name_translations": { "de": "Flexible Bedingungen" }, "rules": [ { "days_before_start": 7, "action": "admin_task", "fee_percent": 50 } ] }, "id": "cancellationconditiongroup_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/cancellationcondition" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name_translations\":{\"de\":\"Flexible Bedingungen\"},\"rules\":[{\"days_before_start\":7,\"action\":\"admin_task\",\"fee_percent\":50}]},\"id\":\"cancellationconditiongroup_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_cancellationcondition","summary":"Stornierungsgruppe und Regelwerk ändern","description":"Ändert eine vorhandene Gruppe; data darf nichtleer sein. Ein übergebenes rules-Array ersetzt das vollständige Regelwerk; [] löscht alle Regeln. Passende Regel-IDs aktualisieren bestehende Gruppenregeln, sonst werden neue Regeln angelegt. default=true aktiviert die Gruppe und setzt den bisherigen Standard zurück.","tags":["cancellationcondition"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"cancellationconditiongroup_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"name_translations":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"active":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"status":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"default":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"auto_refund":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"refund_method":{"type":"string","description":"online, voucher oder balance; unbekannte Werte werden online."},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"enum":[false,null,"",0,"0"]}],"description":"Öffentliche v2-ID; false, null, leere Zeichenkette, 0 und \"0\" entfernen die Zuordnung."},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"enum":[false,null,"",0,"0"]}],"description":"Öffentliche v2-ID; false, null, leere Zeichenkette, 0 und \"0\" entfernen die Zuordnung."},"withdrawal":{"type":"object","properties":{"enabled":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"checkbox_label":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"checkbox_label_translations":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]}},"additionalProperties":true},"withdrawal_enabled":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"withdrawal_checkbox_label":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."},"action":{"type":"string","description":"automatic oder admin_task; sonst admin_task."},"action_type":{"type":"string","description":"Alias für action."},"fee_percent":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."},"active":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"status":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"sort_order":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."}},"additionalProperties":true,"description":"Negative Tage werden 0; Prozentwerte werden auf 0..100 begrenzt. id aktualisiert nur eine bestehende Regel dieser Gruppe."}}},"additionalProperties":true,"minProperties":1},"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Stornierungsgruppe und Regelwerk ändern","value":{"data":{"name_translations":{"de":"Flexible Bedingungen"},"rules":[{"days_before_start":7,"action":"admin_task","fee_percent":50}]},"id":"cancellationconditiongroup_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Cancellationcondition.php","method":"updateCancellationcondition","line":89},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"cancellationcondition","short":"caco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/cancellationcondition/{id} Stornierungsgruppe entfernen Markiert Gruppe und Regeln als gelöscht und löst Angebotszuordnungen. Die aktuelle Standardgruppe kann nicht gelöscht werden. - Operation-ID: delete_v2_cancellationcondition_id - Dokumentation: /docs/api/v2/operations/delete-v2-cancellationcondition-id-7836d9135ad5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche cancellationconditiongroup-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche cancellationconditiongroup-ID.","schema":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/cancellationcondition/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/cancellationcondition/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/cancellationcondition/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_cancellationcondition_id","summary":"Stornierungsgruppe entfernen","description":"Markiert Gruppe und Regeln als gelöscht und löst Angebotszuordnungen. Die aktuelle Standardgruppe kann nicht gelöscht werden.","tags":["cancellationcondition"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche cancellationconditiongroup-ID.","schema":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"cancellationconditiongroup_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"Stornierungsgruppe entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Cancellationcondition.php","method":"deleteCancellationcondition","line":139},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"cancellationcondition","short":"caco","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/cancellationcondition/{id} Stornierungsgruppen und Widerrufsregeln abrufen Liest eine Gruppe per Pfad-ID bzw. id-Filter oder eine nach Gruppen-ID indizierte Map. Vor dem Lesen werden fehlende Standardgruppen angelegt und der Standardstatus sichergestellt. - Operation-ID: get_v2_cancellationcondition_id - Dokumentation: /docs/api/v2/operations/get-v2-cancellationcondition-id-e6acbe0fcfff/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche cancellationconditiongroup-ID. | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche cancellationconditiongroup-ID.","schema":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"cancellationconditiongroup_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/cancellationcondition/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/cancellationcondition/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/cancellationcondition/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_cancellationcondition_id","summary":"Stornierungsgruppen und Widerrufsregeln abrufen","description":"Liest eine Gruppe per Pfad-ID bzw. id-Filter oder eine nach Gruppen-ID indizierte Map. Vor dem Lesen werden fehlende Standardgruppen angelegt und der Standardstatus sichergestellt.","tags":["cancellationcondition"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche cancellationconditiongroup-ID.","schema":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"cancellationconditiongroup_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Gruppenobjekt beim gültigen id-Selektor, sonst ID-Map; leere PHP-Map wird als [] ausgegeben.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"cancellationconditiongroup_012345abcdef_123456","name":"Flexible Stornierung","name_translations":{"de":"Flexible Stornierung"},"slug":"flexible-stornierung","active":true,"default":false,"auto_refund":true,"refund_method":"online","legaltext":false,"vat":false,"withdrawal":{"enabled":true,"url":"https://app2.edoobox.com/EXAMPLE/?edwithdrawal=cancellationconditiongroup_012345abcdef_123456","checkbox_label":"Beispiel einer Widerrufsbestätigung","checkbox_label_translations":{"de":"Beispiel einer Widerrufsbestätigung"}},"rules":[{"id":"cancellationconditionrule_012345abcdef_123456","days_before_start":14,"action":"automatic","fee_percent":25,"active":true,"sort_order":0}],"compact_table_html":"
14 Tage25 %
"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Cancellationcondition.php","line":190},"reason":"GET ensures default groups/configuration and can INSERT missing rows (Dunning.php206)."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Cancellationcondition.php","method":"getCancellationcondition","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"cancellationcondition","short":"caco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/cancellationcondition/{id} Stornierungsgruppe und Regelwerk ändern Ändert eine vorhandene Gruppe; data darf nichtleer sein. Ein übergebenes rules-Array ersetzt das vollständige Regelwerk; [] löscht alle Regeln. Passende Regel-IDs aktualisieren bestehende Gruppenregeln, sonst werden neue Regeln angelegt. default=true aktiviert die Gruppe und setzt den bisherigen Standard zurück. - Operation-ID: put_v2_cancellationcondition_id - Dokumentation: /docs/api/v2/operations/put-v2-cancellationcondition-id-a053810f2068/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche cancellationconditiongroup-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche cancellationconditiongroup-ID.","schema":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name_translations": { "de": "Flexible Bedingungen" }, "rules": [ { "days_before_start": 7, "action": "admin_task", "fee_percent": 50 } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/cancellationcondition/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/cancellationcondition/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name_translations": { "de": "Flexible Bedingungen" }, "rules": [ { "days_before_start": 7, "action": "admin_task", "fee_percent": 50 } ] } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/cancellationcondition/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name_translations\":{\"de\":\"Flexible Bedingungen\"},\"rules\":[{\"days_before_start\":7,\"action\":\"admin_task\",\"fee_percent\":50}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_cancellationcondition_id","summary":"Stornierungsgruppe und Regelwerk ändern","description":"Ändert eine vorhandene Gruppe; data darf nichtleer sein. Ein übergebenes rules-Array ersetzt das vollständige Regelwerk; [] löscht alle Regeln. Passende Regel-IDs aktualisieren bestehende Gruppenregeln, sonst werden neue Regeln angelegt. default=true aktiviert die Gruppe und setzt den bisherigen Standard zurück.","tags":["cancellationcondition"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche cancellationconditiongroup-ID.","schema":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"cancellationconditiongroup_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"name_translations":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"active":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"status":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"default":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"auto_refund":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"refund_method":{"type":"string","description":"online, voucher oder balance; unbekannte Werte werden online."},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"enum":[false,null,"",0,"0"]}],"description":"Öffentliche v2-ID; false, null, leere Zeichenkette, 0 und \"0\" entfernen die Zuordnung."},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"enum":[false,null,"",0,"0"]}],"description":"Öffentliche v2-ID; false, null, leere Zeichenkette, 0 und \"0\" entfernen die Zuordnung."},"withdrawal":{"type":"object","properties":{"enabled":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"checkbox_label":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"checkbox_label_translations":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]}},"additionalProperties":true},"withdrawal_enabled":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"withdrawal_checkbox_label":{"anyOf":[{"type":"string","description":"Text in Kontosprache oder JSON-kodierte Übersetzungsmap."},{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}}]},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."},"action":{"type":"string","description":"automatic oder admin_task; sonst admin_task."},"action_type":{"type":"string","description":"Alias für action."},"fee_percent":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."},"active":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"status":{"type":["boolean","number","string","null"],"description":"Nur true, 1, \"1\", \"true\" und \"y\" aktivieren; andere Werte werden false."},"sort_order":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."}},"additionalProperties":true,"description":"Negative Tage werden 0; Prozentwerte werden auf 0..100 begrenzt. id aktualisiert nur eine bestehende Regel dieser Gruppe."}}},"additionalProperties":true,"minProperties":1},"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Stornierungsgruppe und Regelwerk ändern","value":{"data":{"name_translations":{"de":"Flexible Bedingungen"},"rules":[{"days_before_start":7,"action":"admin_task","fee_percent":50}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Cancellationcondition.php","method":"updateCancellationcondition","line":89},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"cancellationcondition","short":"caco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/cancellationcondition/list Stornierungsgruppen und Widerrufsregeln abrufen Liest eine Gruppe per Pfad-ID bzw. id-Filter oder eine nach Gruppen-ID indizierte Map. Vor dem Lesen werden fehlende Standardgruppen angelegt und der Standardstatus sichergestellt. - Operation-ID: get_v2_cancellationcondition_list - Dokumentation: /docs/api/v2/operations/get-v2-cancellationcondition-list-0d137c0e9b20/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"cancellationconditiongroup_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/cancellationcondition/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/cancellationcondition/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/cancellationcondition/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_cancellationcondition_list","summary":"Stornierungsgruppen und Widerrufsregeln abrufen","description":"Liest eine Gruppe per Pfad-ID bzw. id-Filter oder eine nach Gruppen-ID indizierte Map. Vor dem Lesen werden fehlende Standardgruppen angelegt und der Standardstatus sichergestellt.","tags":["cancellationcondition"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"cancellationconditiongroup_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Gruppenobjekt beim gültigen id-Selektor, sonst ID-Map; leere PHP-Map wird als [] ausgegeben.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"cancellationconditiongroup_012345abcdef_123456":{"id":"cancellationconditiongroup_012345abcdef_123456","name":"Flexible Stornierung","name_translations":{"de":"Flexible Stornierung"},"slug":"flexible-stornierung","active":true,"default":false,"auto_refund":true,"refund_method":"online","legaltext":false,"vat":false,"withdrawal":{"enabled":true,"url":"https://app2.edoobox.com/EXAMPLE/?edwithdrawal=cancellationconditiongroup_012345abcdef_123456","checkbox_label":"Beispiel einer Widerrufsbestätigung","checkbox_label_translations":{"de":"Beispiel einer Widerrufsbestätigung"}},"rules":[{"id":"cancellationconditionrule_012345abcdef_123456","days_before_start":14,"action":"automatic","fee_percent":25,"active":true,"sort_order":0}],"compact_table_html":"
14 Tage25 %
"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string"},"active":{"type":"boolean"},"default":{"type":"boolean"},"auto_refund":{"type":"boolean"},"refund_method":{"type":"string"},"legaltext":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","examples":["legaltext_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"withdrawal":{"type":"object","properties":{"enabled":{"type":"boolean"},"url":{"type":"string"},"checkbox_label":{"type":"string"},"checkbox_label_translations":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^cancellationconditionrule_[a-f0-9]{12}_-?[0-9]+$","examples":["cancellationconditionrule_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"days_before_start":{"type":"integer"},"action":{"type":"string"},"fee_percent":{"type":"number"},"active":{"type":"boolean"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"compact_table_html":{"type":"string","description":"HTML-Regelübersicht."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Cancellationcondition.php","line":17,"lineEnd":47,"sha256":"cb977f060aec4ede84b8098dcf7e7ae00414568d101f3e8879383918e7317ff4","reason":"GET invokes ensureDefaultGroups(fid) before selecting/formatting account groups."},"reason":"Reads cancellation groups but seeds missing built-in groups/rules and repairs default-group invariants first.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/19","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/19","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Cancellationcondition.php","method":"getCancellationcondition","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"cancellationcondition","short":"caco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/category Kategorien auflisten Liest Kategorien im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert Kategorieordner (type=folder), keine Angebote oder Systemwurzeln; endgültig gelöschte Einträge werden ausgeschlossen. - Operation-ID: get_v2_category - Dokumentation: /docs/api/v2/operations/get-v2-category-775ac7a79858/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/category' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_category","summary":"Kategorien auflisten","description":"Liest Kategorien im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert Kategorieordner (type=folder), keine Angebote oder Systemwurzeln; endgültig gelöschte Einträge werden ausgeschlossen.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"category_012345abcdef_123456":{"id":"category_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"getCategory","line":550},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/category Kategorie anlegen Legt Kategorie mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. name darf zusätzlich als Liste von {language,value} übergeben werden; die Kontostandardsprache muss enthalten sein, weitere aktive Kontosprachen werden nachgetragen. Baum- und Abo-Regeln werden serverseitig geprüft. type muss eine Kategorie ergeben (folder bzw. normalisierte Standardangabe); über diesen Endpunkt entstehen keine Angebote oder Systemwurzeln. Der Parent muss eine zulässige Kategorie bzw. Wurzel mit konsistentem Archivstatus sein. - Operation-ID: post_v2_category - Dokumentation: /docs/api/v2/operations/post-v2-category-dfa13e5a7716/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example category", "category": "category_012345abcdef_123456", "type": "folder" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/category' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example category", "category": "category_012345abcdef_123456", "type": "folder" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example category\",\"category\":\"category_012345abcdef_123456\",\"type\":\"folder\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_category","summary":"Kategorie anlegen","description":"Legt Kategorie mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. name darf zusätzlich als Liste von {language,value} übergeben werden; die Kontostandardsprache muss enthalten sein, weitere aktive Kontosprachen werden nachgetragen. Baum- und Abo-Regeln werden serverseitig geprüft. type muss eine Kategorie ergeben (folder bzw. normalisierte Standardangabe); über diesen Endpunkt entstehen keine Angebote oder Systemwurzeln. Der Parent muss eine zulässige Kategorie bzw. Wurzel mit konsistentem Archivstatus sein.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":30},"row_lft":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":58,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_rgt":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":68,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":78,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"image":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":113},"type":{"x-edoobox-knownInputs":["offer","folder","archive","trash"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":133},"design":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":172},"internal_code":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":40,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":188},"prevent_multiple_bookings":{"x-edoobox-recognizedValues":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"],"description":"false clears; recognized participant-data key maps to its DB column. Unknown string returns null rather than explicit rejection. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nFalsy input -> empty DB text. Known key -> Datastructure column. Unknown key falls through null. Empty output -> false; known column -> public key; unmapped nonempty -> null.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["email",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":221,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"trash":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":233,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","category","type"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":30},"row_lft":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":58,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_rgt":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":68,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":78,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"image":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":113},"type":{"x-edoobox-knownInputs":["offer","folder","archive","trash"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":133},"design":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":172},"internal_code":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":40,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":188},"prevent_multiple_bookings":{"x-edoobox-recognizedValues":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"],"description":"false clears; recognized participant-data key maps to its DB column. Unknown string returns null rather than explicit rejection. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nFalsy input -> empty DB text. Known key -> Datastructure column. Unknown key falls through null. Empty output -> false; known column -> public key; unmapped nonempty -> null.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["email",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":221,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"trash":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":233,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","category","type"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"name":"Example category","category":"category_012345abcdef_123456","type":"folder"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"postCategory","line":268},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/category Kategorie aktualisieren Ändert die übergebenen schreibbaren Felder in data für Kategorie. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. - Operation-ID: put_v2_category - Dokumentation: /docs/api/v2/operations/put-v2-category-7d12f46ada34/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "design": "design_012345abcdef_123456" }, "id": "category_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/category' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "design": "design_012345abcdef_123456" }, "id": "category_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"design\":\"design_012345abcdef_123456\"},\"id\":\"category_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_category","summary":"Kategorie aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Kategorie. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":false,"row_lft":false,"row_rgt":false,"order":false,"image":false,"category":false,"type":false,"design":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":172},"internal_code":{"x-edoobox-canonicalType":"string","description":"aP(...,true,40) trims, flattens line breaks, escapes and truncates strings; nonstrings become empty. Longer input is not rejected.\n\nString normalized to at most 40 characters after escaping. Category propagates to descendants; both disable firmaConnect.\n\nRuntime preconditions / rejection: Nonempty code requires subscription internal_function; absence emits ED40240. Missing/wrong-kind resource: category callback false -> ED90100; offer throws offer_not_found.","x-edoobox-schemaStatus":"source-reviewed","examples":["INTERNAL-SAMPLE",""],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"prevent_multiple_bookings":{"x-edoobox-recognizedValues":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"],"description":"false clears; recognized participant-data key maps to its DB column. Unknown string returns null rather than explicit rejection. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nFalsy input -> empty DB text. Known key -> Datastructure column. Unknown key falls through null. Empty output -> false; known column -> public key; unmapped nonempty -> null.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["email",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":false,"trash":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["name","row_lft","row_rgt","order","image","category","type","archive","trash"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Kategorie aktualisieren","value":{"data":{"design":"design_012345abcdef_123456"},"id":"category_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"updateCategory","line":373},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/category/{id} Kategorie löschen Löscht Kategorie im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Löscht den vollständigen erreichbaren Teilbaum logisch und entfernt ihn aus dem aktiven Baum. Statusregeln können die Löschung blockieren; data.delete=false und error_code beachten. - Operation-ID: delete_v2_category_id - Dokumentation: /docs/api/v2/operations/delete-v2-category-id-79070e9cbdc7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "category_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "category_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"category_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_category_id","summary":"Kategorie löschen","description":"Löscht Kategorie im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Löscht den vollständigen erreichbaren Teilbaum logisch und entfernt ihn aus dem aktiven Baum. Statusregeln können die Löschung blockieren; data.delete=false und error_code beachten.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"delete":{"type":"boolean"},"error_code":{"type":"string"},"block_offers":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Kategorie löschen","value":{"id":"category_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"deleteCategory","line":404},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/category/{id} Kategorie abrufen Liest Kategorie anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Liefert Kategorieordner (type=folder), keine Angebote oder Systemwurzeln; endgültig gelöschte Einträge werden ausgeschlossen. - Operation-ID: get_v2_category_id - Dokumentation: /docs/api/v2/operations/get-v2-category-id-5e1e737e2ff2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_category_id","summary":"Kategorie abrufen","description":"Liest Kategorie anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Liefert Kategorieordner (type=folder), keine Angebote oder Systemwurzeln; endgültig gelöschte Einträge werden ausgeschlossen.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"category_012345abcdef_123456":{"id":"category_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"getCategory","line":550},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/category/{id} Kategorie aktualisieren Ändert die übergebenen schreibbaren Felder in data für Kategorie. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. - Operation-ID: put_v2_category_id - Dokumentation: /docs/api/v2/operations/put-v2-category-id-ee60b183feeb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "design": "design_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "design": "design_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"design\":\"design_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_category_id","summary":"Kategorie aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Kategorie. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":false,"row_lft":false,"row_rgt":false,"order":false,"image":false,"category":false,"type":false,"design":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":172},"internal_code":{"x-edoobox-canonicalType":"string","description":"aP(...,true,40) trims, flattens line breaks, escapes and truncates strings; nonstrings become empty. Longer input is not rejected.\n\nString normalized to at most 40 characters after escaping. Category propagates to descendants; both disable firmaConnect.\n\nRuntime preconditions / rejection: Nonempty code requires subscription internal_function; absence emits ED40240. Missing/wrong-kind resource: category callback false -> ED90100; offer throws offer_not_found.","x-edoobox-schemaStatus":"source-reviewed","examples":["INTERNAL-SAMPLE",""],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"x-edoobox-recognizedValues":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"],"description":"false clears; recognized participant-data key maps to its DB column. Unknown string returns null rather than explicit rejection. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nFalsy input -> empty DB text. Known key -> Datastructure column. Unknown key falls through null. Empty output -> false; known column -> public key; unmapped nonempty -> null.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["email",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":false,"trash":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["name","row_lft","row_rgt","order","image","category","type","archive","trash"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"design":"design_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"updateCategory","line":373},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/category/{id}/auto_delete Kategorie in den Papierkorb verschieben Verschiebt Kategorie samt zulässigem Teilbaum in den Papierkorb. Die Ziel-ID genügt; data wird nicht gelesen. Bereits verschobene Einträge, fehlende Papierkorbwurzel und nicht erlaubte Angebotsstatus werden als update=false mit error_code gemeldet. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_category_id_auto_delete - Dokumentation: /docs/api/v2/operations/put-v2-category-id-auto-delete-72461efc808f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'/auto_delete' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}/auto_delete"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}/auto_delete" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_category_id_auto_delete","summary":"Kategorie in den Papierkorb verschieben","description":"Verschiebt Kategorie samt zulässigem Teilbaum in den Papierkorb. Die Ziel-ID genügt; data wird nicht gelesen. Bereits verschobene Einträge, fehlende Papierkorbwurzel und nicht erlaubte Angebotsstatus werden als update=false mit error_code gemeldet. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"update":{"type":"boolean"},"error_code":{"type":"string"},"block_offers":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Kategorie in den Papierkorb verschieben","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"updateCategory","line":373,"branch":{"file":"application/models/v2/Category.php","line":387}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/category/{id}/dashboard Kategorieübersicht mit Buchungsstatistik abrufen Liefert die Kategorie, Angebots-/Buchungszahlen und Umsatzsummen je Währung für ihren Teilbaum sowie Promotiondaten. chart_line enthält fest vorgegebene Buchungs-/Gutscheinmetriken der letzten vier Wochen. Die Aggregation trennt archivierte und aktive Teilbäume; fields, metrics und Listenpaginierung ändern den Dashboardumfang nicht. - Operation-ID: get_v2_category_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-category-id-dashboard-bb81c88ea00d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_category_id_dashboard","summary":"Kategorieübersicht mit Buchungsstatistik abrufen","description":"Liefert die Kategorie, Angebots-/Buchungszahlen und Umsatzsummen je Währung für ihren Teilbaum sowie Promotiondaten. chart_line enthält fest vorgegebene Buchungs-/Gutscheinmetriken der letzten vier Wochen. Die Aggregation trennt archivierte und aktive Teilbäume; fields, metrics und Listenpaginierung ändern den Dashboardumfang nicht.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"category_id":"category_012345abcdef_123456","category":{"id":"category_012345abcdef_123456","name":"Beispielkategorie"},"category_stats":{"count_offer":1,"count_booking":8,"sum_amount":[{"total":240,"currency_iso":"CHF"}],"chart_line":{}},"promotions":false,"performing_promotion":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"category_id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"category":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true},{"const":false}]},"category_stats":{"type":"object","properties":{"count_offer":{"type":"integer"},"count_booking":{"type":"integer"},"sum_amount":{"type":"array","items":{"type":"object","properties":{"total":{"type":"number"},"currency_iso":{"type":"string"}},"additionalProperties":true,"required":["total","currency_iso"]}},"chart_line":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true,"required":["count_offer","count_booking","sum_amount","chart_line"]},"promotions":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"performing_promotion":{"type":"array","items":{"type":"object","properties":{"promotion_id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"assigned":{"type":"integer"}},"additionalProperties":true,"required":["promotion_id","assigned"]}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/dashboard/Categorydashboard.php","line":11},"reason":"Complete reachable dashboard graph reviewed: SELECT aggregations, existing links, request-local formatting and serializers. Nested Eddata reads clear action segments and do not reach tree repair or other mutation branches. Derived read caches are not domain changes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"getCategory","line":550,"branch":{"file":"application/models/v2/Category.php","line":568}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/category/{id}/empty Papierkorb leeren Mit dem literalen Pfadwert trash werden alle Einträge unter der Papierkorbwurzel endgültig logisch gelöscht und aus dem aktiven Baum entfernt. Die Wurzel bleibt erhalten. Andere Pfadwerte gelangen in die normale Kategorie-Löschung; sie führen keine Papierkorbleerung aus. - Operation-ID: delete_v2_category_id_empty - Dokumentation: /docs/api/v2/operations/delete-v2-category-id-empty-4dfdfdd3d18e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | trash für die Papierkorbleerung; eine Kategorie-ID löst stattdessen die normale Kategorie-Löschung aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"trash für die Papierkorbleerung; eine Kategorie-ID löst stattdessen die normale Kategorie-Löschung aus.","schema":{"anyOf":[{"const":"trash"},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'/empty' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}/empty"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}/empty" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_category_id_empty","summary":"Papierkorb leeren","description":"Mit dem literalen Pfadwert trash werden alle Einträge unter der Papierkorbwurzel endgültig logisch gelöscht und aus dem aktiven Baum entfernt. Die Wurzel bleibt erhalten. Andere Pfadwerte gelangen in die normale Kategorie-Löschung; sie führen keine Papierkorbleerung aus.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"trash für die Papierkorbleerung; eine Kategorie-ID löst stattdessen die normale Kategorie-Löschung aus.","schema":{"anyOf":[{"const":"trash"},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"trash","empty":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"const":"trash"},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]}]},"empty":{"type":"boolean"},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"anyOf":[{"const":"trash"},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]}]}},"additionalProperties":true},"examples":{"request":{"summary":"Papierkorb leeren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"deleteCategory","line":404,"branch":{"file":"application/models/v2/Category.php","line":409}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/category/{id}/image Kategorie mit einem Bild versehen Wählt eine kontoeigene Publicfile-Datei als Bild aus und erstellt bzw. verwendet die erforderlichen Bildvarianten. Erlaubt sind JPG, JPEG und PNG; publicfile=false entfernt das Bild. Der Endpunkt verarbeitet keine direkten Datei-Bytes. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_category_id_image - Dokumentation: /docs/api/v2/operations/put-v2-category-id-image-8ee5afa5960f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "publicfile": "publicfile_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'/image' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}/image"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "publicfile": "publicfile_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}/image" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"publicfile\":\"publicfile_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_category_id_image","summary":"Kategorie mit einem Bild versehen","description":"Wählt eine kontoeigene Publicfile-Datei als Bild aus und erstellt bzw. verwendet die erforderlichen Bildvarianten. Erlaubt sind JPG, JPEG und PNG; publicfile=false entfernt das Bild. Der Endpunkt verarbeitet keine direkten Datei-Bytes. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","update":true,"image":"beispiel.jpg","files":[],"url":"https://cdn.example.invalid/beispiel.jpg"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"update":{"type":"boolean"},"image":{"anyOf":[{"type":"string"},{"const":false}]},"files":{"type":"array","items":{"type":"string"}},"url":{"type":"string"}},"additionalProperties":true,"required":["id","update","image"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"publicfile":{"anyOf":[{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},{"const":false}]}},"additionalProperties":true,"required":["publicfile"]},"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","examples":["category_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kategorie mit einem Bild versehen","value":{"data":{"publicfile":"publicfile_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"updateCategory","line":373,"branch":{"file":"application/models/v2/Category.php","line":383}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/category/{id}/move Kategorie im Baum verschieben Verschiebt den vollständigen Knoten unter node (inside) bzw. vor/nach node (before/after). node ist eine öffentliche Kategorie- oder Angebots-ID. Konto-, Zyklus-, Status-, Archiv-, Papierkorb- und Multi-Angebotsregeln werden unter Baumsperre geprüft; ein abgelehntes Ergebnis kann error=false mit update=false enthalten. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_category_id_move - Dokumentation: /docs/api/v2/operations/put-v2-category-id-move-7d7891371b71/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "node": "category_012345abcdef_123456", "position": "inside" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'/move' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}/move"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "node": "category_012345abcdef_123456", "position": "inside" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}/move" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"node\":\"category_012345abcdef_123456\",\"position\":\"inside\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_category_id_move","summary":"Kategorie im Baum verschieben","description":"Verschiebt den vollständigen Knoten unter node (inside) bzw. vor/nach node (before/after). node ist eine öffentliche Kategorie- oder Angebots-ID. Konto-, Zyklus-, Status-, Archiv-, Papierkorb- und Multi-Angebotsregeln werden unter Baumsperre geprüft; ein abgelehntes Ergebnis kann error=false mit update=false enthalten. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"update":{"type":"boolean"},"error_code":{"type":"string"},"block_offers":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"node":{"anyOf":[{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}],"description":"Öffentliche Kategorie- oder Angebots-ID des Zielknotens; der gewählte position-Wert bestimmt die Beziehung."},"position":{"type":"string","enum":["before","after","inside"]}},"additionalProperties":true,"required":["node","position"]},"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","examples":["category_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kategorie im Baum verschieben","value":{"data":{"node":"category_012345abcdef_123456","position":"inside"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"updateCategory","line":373,"branch":{"file":"application/models/v2/Category.php","line":379}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/category/{id}/shareurl Freigabe- und Buchungslinks für Kategorie abrufen Ermittelt die vorhandenen Buchungs-, Einbettungs- und Trackinglinks sowie den internen Zugangscode. Bei Kindern eines Multi-Angebots beziehen sich die Links auf das übergeordnete Multi-Angebot; data.id bleibt die angefragte ID. Die Operation erstellt keine Buchung. Die Pfad-ID wird als id-Filter ausgewertet. - Operation-ID: get_v2_category_id_shareurl - Dokumentation: /docs/api/v2/operations/get-v2-category-id-shareurl-426a1d58910d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | Optional; die Pfad-ID wird zuerst eingesetzt. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"Optional; die Pfad-ID wird zuerst eingesetzt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"category_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/category/'"${PATH_ID}"'/shareurl' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/{id}/shareurl"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/{id}/shareurl" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_category_id_shareurl","summary":"Freigabe- und Buchungslinks für Kategorie abrufen","description":"Ermittelt die vorhandenen Buchungs-, Einbettungs- und Trackinglinks sowie den internen Zugangscode. Bei Kindern eines Multi-Angebots beziehen sich die Links auf das übergeordnete Multi-Angebot; data.id bleibt die angefragte ID. Die Operation erstellt keine Buchung. Die Pfad-ID wird als id-Filter ausgewertet.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"Optional; die Pfad-ID wird zuerst eingesetzt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"category_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","shareurl":{"internal_code":"","url":{"base":"https://app.edoobox.com/","booknow":"https://example.invalid/book","segment":"beispiel","gtm":"https://example.invalid/tracking","iframe":"","shortcut":"beispiel"},"data":{"iframe":false}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"shareurl":{"type":"object","properties":{"internal_code":{"type":"string"},"url":{"type":"object","properties":{"base":{"type":"string"},"booknow":{"type":"string"},"segment":{"type":"string"},"gtm":{"type":"string"},"iframe":{"type":"string"},"shortcut":{"type":"string"}},"additionalProperties":true},"data":{"type":"object","properties":{"iframe":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true,"required":["id","shareurl"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Category.php","line":564,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/20","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Category.php","line":550,"lineEnd":567,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","reason":"shareurl dispatches to Offerfunc::_getShareWinUrl."},"reason":"Builds share URLs from existing category/account settings and signs data URLs without persisting a grant.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/20","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"getCategory","line":550,"branch":{"file":"application/models/v2/Category.php","line":564}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/category/list Kategorien auflisten Liest Kategorien im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert Kategorieordner (type=folder), keine Angebote oder Systemwurzeln; endgültig gelöschte Einträge werden ausgeschlossen. - Operation-ID: get_v2_category_list - Dokumentation: /docs/api/v2/operations/get-v2-category-list-08041d608522/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/category/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_category_list","summary":"Kategorien auflisten","description":"Liest Kategorien im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert Kategorieordner (type=folder), keine Angebote oder Systemwurzeln; endgültig gelöschte Einträge werden ausgeschlossen.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"category_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"category_012345abcdef_123456":{"id":"category_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":30,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":58,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":95,"lineStart":95,"lineEnd":112,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"1232dab7d815f052901c5335ecf83df35dc6063e4fded1a864d8de16b8aee621","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":172,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":188,"lineStart":188,"lineEnd":197,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"b9cc8df6ed7c8b88c9502b24e5a2d80c31b50d6c8c147413d3017e2fcce84c4d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Categoryfunc.php","line":19,"lineStart":19,"lineEnd":77,"sha256":"dd87dd957aef94579c4884c9fc846477cfb33cdac923a22a4885ad995871751c","spanSha256":"71831f2331b841d77fd50063ba1b55109814f8917f9b1fb9e39a81165b4d2c1c","reason":"func delegates to _internal_code"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"prevent_multiple_bookings":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Category.php","line":198,"lineStart":198,"lineEnd":220,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"101039fe8703832f5080f0af566ed7113ce1b2584974b72c122f070794ea90c6","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Datastructure.php","line":20,"lineStart":20,"lineEnd":126,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","spanSha256":"e0b745f0330dae128c01bcc320aa6dc4429d391260fee2e2d6964a611041e2a9","reason":"Known participant keys plus data_1..data_30 generation and returnStructure."},{"file":"application/models/v2/Category.php","line":268,"lineStart":268,"lineEnd":303,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"3b64150dba0beb6abedf5061f2b9e11f36159e56028ca8e00171a15d93302784","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Category.php","line":373,"lineStart":373,"lineEnd":394,"sha256":"43e78dafbef58fdc0de2a46c4a9831d7615225c221164d9c5a0f6fabc7fc9e45","spanSha256":"6ed1a2030b3f277a83ece5878a285d06b4a16597da58e1ae03fb27fe5f35f27c","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":233,"type":"boolean"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"getCategory","line":550},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/category/list/shareurl Freigabe- und Buchungslinks für Kategorie abrufen Ermittelt die vorhandenen Buchungs-, Einbettungs- und Trackinglinks sowie den internen Zugangscode. Bei Kindern eines Multi-Angebots beziehen sich die Links auf das übergeordnete Multi-Angebot; data.id bleibt die angefragte ID. Die Operation erstellt keine Buchung. filter muss einen Eintrag mit property=id und einer einzelnen öffentlichen category-ID enthalten. - Operation-ID: get_v2_category_list_shareurl - Dokumentation: /docs/api/v2/operations/get-v2-category-list-shareurl-7ba47ced201a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste; ein einzelner id-Filter ist für diese Sammlung erforderlich. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste; ein einzelner id-Filter ist für diese Sammlung erforderlich.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"category_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/category/list/shareurl' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/category/list/shareurl"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/category/list/shareurl" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_category_list_shareurl","summary":"Freigabe- und Buchungslinks für Kategorie abrufen","description":"Ermittelt die vorhandenen Buchungs-, Einbettungs- und Trackinglinks sowie den internen Zugangscode. Bei Kindern eines Multi-Angebots beziehen sich die Links auf das übergeordnete Multi-Angebot; data.id bleibt die angefragte ID. Die Operation erstellt keine Buchung. filter muss einen Eintrag mit property=id und einer einzelnen öffentlichen category-ID enthalten.","tags":["category"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste; ein einzelner id-Filter ist für diese Sammlung erforderlich.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"category_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"category_012345abcdef_123456","shareurl":{"internal_code":"","url":{"base":"https://app.edoobox.com/","booknow":"https://example.invalid/book","segment":"beispiel","gtm":"https://example.invalid/tracking","iframe":"","shortcut":"beispiel"},"data":{"iframe":false}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},"shareurl":{"type":"object","properties":{"internal_code":{"type":"string"},"url":{"type":"object","properties":{"base":{"type":"string"},"booknow":{"type":"string"},"segment":{"type":"string"},"gtm":{"type":"string"},"iframe":{"type":"string"},"shortcut":{"type":"string"}},"additionalProperties":true},"data":{"type":"object","properties":{"iframe":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true,"required":["id","shareurl"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Category.php","method":"getCategory","line":550,"branch":{"file":"application/models/v2/Category.php","line":564}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"category","short":"ctgy","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/charts/list/donut/offer Meistgebuchte Angebote mit Impressionen lesen Ermittelt höchstens zehn Angebote nach Buchungsanzahl im Zeitraum und ergänzt Impressionen. Der Zeitraum begrenzt die Buchungen; die aktuelle Abfrage begrenzt die Impressionen nicht nochmals auf diesen Zeitraum. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein. - Operation-ID: get_v2_charts_list_donut_offer - Dokumentation: /docs/api/v2/operations/get-v2-charts-list-donut-offer-6ce6201318a5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/charts/list/donut/offer' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/charts/list/donut/offer"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/charts/list/donut/offer" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_charts_list_donut_offer","summary":"Meistgebuchte Angebote mit Impressionen lesen","description":"Ermittelt höchstens zehn Angebote nach Buchungsanzahl im Zeitraum und ergänzt Impressionen. Der Zeitraum begrenzt die Buchungen; die aktuelle Abfrage begrenzt die Impressionen nicht nochmals auf diesen Zeitraum. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein.","tags":["charts"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält columns und rows im Format [Buchungen,Impressionen,Angebots-v2-ID].","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"bookings","type":"integer"},{"field":"impressions","type":"integer"},{"field":"offer","type":"string"}],"rows":[[8,120,"offer_012345abcdef_123456"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Charts.php","line":66},"reason":"Delegates to SELECT-only chart aggregation models; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Charts.php","method":"getCharts","line":66,"branch":{"file":"application/models/v2/Charts.php","line":113}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"charts","short":"chrt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/charts/list/donut/user Aktive Teilnehmer nach Sprache zählen Gruppiert aktive, nicht gelöschte Teilnehmer des Kontos nach Sprache. Datumsfilter und Paginierung werden nicht berücksichtigt. - Operation-ID: get_v2_charts_list_donut_user - Dokumentation: /docs/api/v2/operations/get-v2-charts-list-donut-user-333545b36b67/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/charts/list/donut/user' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/charts/list/donut/user"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/charts/list/donut/user" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_charts_list_donut_user","summary":"Aktive Teilnehmer nach Sprache zählen","description":"Gruppiert aktive, nicht gelöschte Teilnehmer des Kontos nach Sprache. Datumsfilter und Paginierung werden nicht berücksichtigt.","tags":["charts"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält columns und rows im Format [Anzahl,Sprache]. chart steht neben data.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"value","type":"integer"},{"field":"language","type":"string"}],"rows":[[12,"de"],[3,"en"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Charts.php","line":66},"reason":"Delegates to SELECT-only chart aggregation models; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Charts.php","method":"getCharts","line":66,"branch":{"file":"application/models/v2/Charts.php","line":109}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"charts","short":"chrt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/charts/list/line/offer Angebotskennzahlen im Zeitverlauf vergleichen Liefert für jede über metrics ausgewählte Kennzahl eine eigene Tabelle. Optional offer und bei Buchungs-/Impressions-/Wartelistenkennzahlen category begrenzen den Datenbereich. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein. - Operation-ID: get_v2_charts_list_line_offer - Dokumentation: /docs/api/v2/operations/get-v2-charts-list-line-offer-a2f535bace3d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | metrics | query | ja | Kommaliste: bookings.created, bookings.started, impressions, vouchers, bookings.waiting-list. Mindestens ein bekannter Name ist erforderlich; unbekannte Namen werden ignoriert. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"metrics","in":"query","required":true,"description":"Kommaliste: bookings.created, bookings.started, impressions, vouchers, bookings.waiting-list. Mindestens ein bekannter Name ist erforderlich; unbekannte Namen werden ignoriert.","schema":{"type":"string","examples":["bookings.created,impressions"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" : "${QUERY_METRICS:?QUERY_METRICS setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/charts/list/line/offer' \ --url-query 'filter='"${QUERY_FILTER}" \ --url-query 'metrics='"${QUERY_METRICS}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/charts/list/line/offer"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); url.searchParams.append("metrics", required("QUERY_METRICS")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/charts/list/line/offer" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"]), ("metrics", os.environ["QUERY_METRICS"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_charts_list_line_offer","summary":"Angebotskennzahlen im Zeitverlauf vergleichen","description":"Liefert für jede über metrics ausgewählte Kennzahl eine eigene Tabelle. Optional offer und bei Buchungs-/Impressions-/Wartelistenkennzahlen category begrenzen den Datenbereich. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein.","tags":["charts"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"metrics","in":"query","required":true,"description":"Kommaliste: bookings.created, bookings.started, impressions, vouchers, bookings.waiting-list. Mindestens ein bekannter Name ist erforderlich; unbekannte Namen werden ignoriert.","schema":{"type":"string","examples":["bookings.created,impressions"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist nach angeforderten Kennzahlnamen indiziert; jede Tabelle enthält columns und rows. chart beschreibt resource=offer/type=line.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"bookings.created":{"columns":[{"field":"value","type":"integer"},{"field":"date","type":"date"}],"rows":[[3,"2027-04-01T10:00:00+02:00"]]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"bookings.created":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"bookings.started":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"impressions":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"vouchers":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"bookings.waiting-list":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Charts.php","line":66},"reason":"Delegates to SELECT-only chart aggregation models; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Charts.php","method":"getCharts","line":66,"branch":{"file":"application/models/v2/Charts.php","line":95}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"charts","short":"chrt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/charts/list/status/booking Buchungen nach Abschlussstatus zählen Zählt starting (noch nicht abgeschlossene Vorgänge), done (abgeschlossene nicht stornierte Buchungen) und cancel (stornierte Buchungen). Optional offer begrenzt auf ein Angebot. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein. - Operation-ID: get_v2_charts_list_status_booking - Dokumentation: /docs/api/v2/operations/get-v2-charts-list-status-booking-74629c387726/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/charts/list/status/booking' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/charts/list/status/booking"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/charts/list/status/booking" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_charts_list_status_booking","summary":"Buchungen nach Abschlussstatus zählen","description":"Zählt starting (noch nicht abgeschlossene Vorgänge), done (abgeschlossene nicht stornierte Buchungen) und cancel (stornierte Buchungen). Optional offer begrenzt auf ein Angebot. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein.","tags":["charts"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält columns und rows im Format [Anzahl,Status]. chart steht neben data.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"value","type":"integer"},{"field":"type","type":"string"}],"rows":[[2,"starting"],[8,"done"],[1,"cancel"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Charts.php","line":66},"reason":"Delegates to SELECT-only chart aggregation models; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Charts.php","method":"getCharts","line":66,"branch":{"file":"application/models/v2/Charts.php","line":102}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"charts","short":"chrt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/charts/list/timeline/booking Begonnene Buchungen im Zeitverlauf lesen Zählt Buchungsvorgänge mit is_unset=y je Tag. Optional offer oder category begrenzt auf Angebot, Unterangebot oder Kategorie-/Mehrangebotsbaum. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein. - Operation-ID: get_v2_charts_list_timeline_booking - Dokumentation: /docs/api/v2/operations/get-v2-charts-list-timeline-booking-acdbc44eb079/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/charts/list/timeline/booking' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/charts/list/timeline/booking"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/charts/list/timeline/booking" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_charts_list_timeline_booking","summary":"Begonnene Buchungen im Zeitverlauf lesen","description":"Zählt Buchungsvorgänge mit is_unset=y je Tag. Optional offer oder category begrenzt auf Angebot, Unterangebot oder Kategorie-/Mehrangebotsbaum. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein.","tags":["charts"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält columns und rows im Format [Anzahl,ISO-8601-Datum]; chart beschreibt Ressource/Diagrammtyp. booking/email ergänzen limit mit Trefferzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"value","type":"integer"},{"field":"date","type":"date"}],"rows":[[3,"2027-04-01T10:00:00+02:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Charts.php","line":66},"reason":"Delegates to SELECT-only chart aggregation models; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Charts.php","method":"getCharts","line":66,"branch":{"file":"application/models/v2/Charts.php","line":75}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"charts","short":"chrt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/charts/list/timeline/email Fehlerfreie E-Mails im Zeitverlauf zählen Zählt E-Mails mit anz_error=0 nach Erstellungsdatum je Tag. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein. - Operation-ID: get_v2_charts_list_timeline_email - Dokumentation: /docs/api/v2/operations/get-v2-charts-list-timeline-email-2100a3019f33/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/charts/list/timeline/email' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/charts/list/timeline/email"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/charts/list/timeline/email" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_charts_list_timeline_email","summary":"Fehlerfreie E-Mails im Zeitverlauf zählen","description":"Zählt E-Mails mit anz_error=0 nach Erstellungsdatum je Tag. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein.","tags":["charts"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält columns und rows im Format [Anzahl,ISO-8601-Datum]; chart beschreibt Ressource/Diagrammtyp. booking/email ergänzen limit mit Trefferzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"value","type":"integer"},{"field":"date","type":"date"}],"rows":[[3,"2027-04-01T10:00:00+02:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Charts.php","line":66},"reason":"Delegates to SELECT-only chart aggregation models; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Charts.php","method":"getCharts","line":66,"branch":{"file":"application/models/v2/Charts.php","line":79}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"charts","short":"chrt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/charts/list/timeline/transaction Buchungsumsätze nach Währung im Zeitverlauf lesen Summiert negative Buchungsbeträge als positive Umsätze je Tag und Währung; nur nicht stornierte, nicht wartende Buchungen mit mustpay=0 werden berücksichtigt. Optional offer begrenzt auf ein Angebot. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein. - Operation-ID: get_v2_charts_list_timeline_transaction - Dokumentation: /docs/api/v2/operations/get-v2-charts-list-timeline-transaction-bb74613b8ddf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter: date_start und date_end sowie currency. Datumstexte werden per strtotime gelesen. currency ist eine Kommaliste aus 3–4 Buchstaben langen Währungscodes. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end sowie currency. Datumstexte werden per strtotime gelesen. currency ist eine Kommaliste aus 3–4 Buchstaben langen Währungscodes.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"},{"property":"currency","value":"CHF,EUR"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/charts/list/timeline/transaction' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/charts/list/timeline/transaction"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/charts/list/timeline/transaction" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_charts_list_timeline_transaction","summary":"Buchungsumsätze nach Währung im Zeitverlauf lesen","description":"Summiert negative Buchungsbeträge als positive Umsätze je Tag und Währung; nur nicht stornierte, nicht wartende Buchungen mit mustpay=0 werden berücksichtigt. Optional offer begrenzt auf ein Angebot. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein.","tags":["charts"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end sowie currency. Datumstexte werden per strtotime gelesen. currency ist eine Kommaliste aus 3–4 Buchstaben langen Währungscodes.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"},{"property":"currency","value":"CHF,EUR"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"columns enthält date und die angeforderten Währungen; rows folgen derselben Reihenfolge. chart steht neben data.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"date","type":"date"},{"field":"CHF","type":"float"},{"field":"EUR","type":"float"}],"rows":[["2027-04-01T10:00:00+02:00",150,80]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Charts.php","line":66},"reason":"Delegates to SELECT-only chart aggregation models; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Charts.php","method":"getCharts","line":66,"branch":{"file":"application/models/v2/Charts.php","line":87}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"charts","short":"chrt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/charts/list/timeline/user Buchende Teilnehmer im Zeitverlauf zählen Gruppiert Teilnehmer mit Buchung im gewählten Zeitraum, je Teilnehmer wird eine Buchung berücksichtigt; dies zählt nicht alle neu angelegten Teilnehmer. Optional offer begrenzt auf ein Angebot. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein. - Operation-ID: get_v2_charts_list_timeline_user - Dokumentation: /docs/api/v2/operations/get-v2-charts-list-timeline-user-c1cf7169d226/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/charts/list/timeline/user' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/charts/list/timeline/user"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/charts/list/timeline/user" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_charts_list_timeline_user","summary":"Buchende Teilnehmer im Zeitverlauf zählen","description":"Gruppiert Teilnehmer mit Buchung im gewählten Zeitraum, je Teilnehmer wird eine Buchung berücksichtigt; dies zählt nicht alle neu angelegten Teilnehmer. Optional offer begrenzt auf ein Angebot. date_start und date_end müssen als Einträge der Filterliste vorhanden und in gültige, von null verschiedene Zeitstempel umwandelbar sein.","tags":["charts"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erforderliche JSON-Filter: date_start und date_end. Datumstexte werden per strtotime gelesen. Optionale Angebots-/Kategorieauswahl erfolgt ebenfalls als Filtereintrag.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string","description":"Diagramm-Zeitfilter werten nur property und value aus."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_start","value":"2027-04-01T00:00:00+02:00"},{"property":"date_end","value":"2027-04-30T23:59:59+02:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält columns und rows im Format [Anzahl,ISO-8601-Datum]; chart beschreibt Ressource/Diagrammtyp. booking/email ergänzen limit mit Trefferzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"value","type":"integer"},{"field":"date","type":"date"}],"rows":[[3,"2027-04-01T10:00:00+02:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","items":{}}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Charts.php","line":66},"reason":"Delegates to SELECT-only chart aggregation models; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Charts.php","method":"getCharts","line":66,"branch":{"file":"application/models/v2/Charts.php","line":83}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"charts","short":"chrt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/country Land auflisten Liest Land-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Nur aktive Länder des globalen Bestands werden ausgegeben; language bestimmt die Sprache der Ländernamen. - Operation-ID: get_v2_country - Dokumentation: /docs/api/v2/operations/get-v2-country-8ead7db170e8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/country' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/country"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/country" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_country","summary":"Land auflisten","description":"Liest Land-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Nur aktive Länder des globalen Bestands werden ausgegeben; language bestimmt die Sprache der Ländernamen.","tags":["country"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"country_012345abcdef_123456":{"id":"country_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},"name":{"type":"string"},"currency_iso_code":{"type":["string","null"]},"currency_num":{"type":["string","number","null"]},"currency_symbol":{"type":["string","null"]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},"name":{"type":"string"},"currency_iso_code":{"type":["string","null"]},"currency_num":{"type":["string","number","null"]},"currency_symbol":{"type":["string","null"]}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Country.php","method":"getCountry","line":147},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"country","short":"ctry","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/country/{id} Land lesen Liest Land anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Nur aktive Länder des globalen Bestands werden ausgegeben; language bestimmt die Sprache der Ländernamen. - Operation-ID: get_v2_country_id - Dokumentation: /docs/api/v2/operations/get-v2-country-id-361064458b50/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/country/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/country/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/country/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_country_id","summary":"Land lesen","description":"Liest Land anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Nur aktive Länder des globalen Bestands werden ausgegeben; language bestimmt die Sprache der Ländernamen.","tags":["country"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"country_012345abcdef_123456":{"id":"country_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},"name":{"type":"string"},"currency_iso_code":{"type":["string","null"]},"currency_num":{"type":["string","number","null"]},"currency_symbol":{"type":["string","null"]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},"name":{"type":"string"},"currency_iso_code":{"type":["string","null"]},"currency_num":{"type":["string","number","null"]},"currency_symbol":{"type":["string","null"]}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Country.php","method":"getCountry","line":147},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"country","short":"ctry","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/country/list Land auflisten Liest Land-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Nur aktive Länder des globalen Bestands werden ausgegeben; language bestimmt die Sprache der Ländernamen. - Operation-ID: get_v2_country_list - Dokumentation: /docs/api/v2/operations/get-v2-country-list-9580fe8db777/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/country/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/country/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/country/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_country_list","summary":"Land auflisten","description":"Liest Land-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Nur aktive Länder des globalen Bestands werden ausgegeben; language bestimmt die Sprache der Ländernamen.","tags":["country"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"country_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"country_012345abcdef_123456":{"id":"country_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},"name":{"type":"string"},"currency_iso_code":{"type":["string","null"]},"currency_num":{"type":["string","number","null"]},"currency_symbol":{"type":["string","null"]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},"name":{"type":"string"},"currency_iso_code":{"type":["string","null"]},"currency_num":{"type":["string","number","null"]},"currency_symbol":{"type":["string","null"]}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Country.php","method":"getCountry","line":147},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"country","short":"ctry","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/datastructure Teilnehmer-Datenfelder und Reihenfolge lesen Liest für Standardfelder und data_1 bis data_30 Sichtbarkeit, Backend-Verwendung, Beschriftung, Sperre, Duplikatsprüfung und Feldart. sort enthält die durch Semikolon getrennten Feldschlüssel. Bei Cache-Miss speichert der Server die erzeugte, sprachabhängige Konfiguration für 84000 Sekunden. - Operation-ID: get_v2_datastructure - Dokumentation: /docs/api/v2/operations/get-v2-datastructure-c54f592bc7a4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/datastructure' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/datastructure"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/datastructure" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_datastructure","summary":"Teilnehmer-Datenfelder und Reihenfolge lesen","description":"Liest für Standardfelder und data_1 bis data_30 Sichtbarkeit, Backend-Verwendung, Beschriftung, Sperre, Duplikatsprüfung und Feldart. sort enthält die durch Semikolon getrennten Feldschlüssel. Bei Cache-Miss speichert der Server die erzeugte, sprachabhängige Konfiguration für 84000 Sekunden.","tags":["datastructure"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data.datafields ist nach Feldschlüsseln indiziert; data.sort ist eine Semikolon-Liste.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"datafields":{"data_1":{"show":true,"lock":false,"duplicate":false,"config":null,"rename":true,"name":"Zusatzangabe","backend":true}},"sort":"first_name;last_name;email;data_1"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"datafields":{"type":"object","additionalProperties":{"type":"object","properties":{"show":{"type":"boolean"},"lock":{"type":"boolean"},"duplicate":{"type":["boolean","null"]},"config":{"anyOf":[{"type":"object","properties":{"type":{"type":"string"}},"additionalProperties":true},{"type":"null"}]},"rename":{"type":"boolean"},"name":{"type":"string"},"backend":{"type":"boolean"}},"additionalProperties":true}},"sort":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/21","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Datastructure.php","line":332,"lineEnd":419,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Cache miss SELECTs stored account form JSON, deserializes with classes disabled, resolves/formats the response and cache->save(84000); no schema/configuration UPDATE."},"reason":"Reads the saved account registration structure and resolves response labels, using an account/language-scoped cache.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/21","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Datastructure.php","method":"getDatastructure","line":332},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"datastructure","short":"dast","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/datastructure Teilnehmer-Datenfeld oder Reihenfolge ändern Ändert die Kontokonfiguration eines Feldschlüssels oder sort. Der Selektor ist ein Standardfeld/data_1 bis data_30 oder sort, keine v2-ID. sort erwartet data.sort als Semikolon-Liste und ergänzt nicht genannte Felder hinten. Gesperrte Felder erlauben nur backend; duplicate=true ist nur bei duplikatfähigen Feldern zulässig. config.type=date/file setzt die Feldart, sonst wird config gelöscht. Das Abonnement muss die Funktion enthalten. Nach dem Speichern werden die Datenstruktur-Caches geleert. - Operation-ID: put_v2_datastructure - Dokumentation: /docs/api/v2/operations/put-v2-datastructure-a04f4244e602/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "show": true, "backend": true, "name": "Zusatzangabe" }, "id": "data_1" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/datastructure' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/datastructure"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "show": true, "backend": true, "name": "Zusatzangabe" }, "id": "data_1" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/datastructure" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"show\":true,\"backend\":true,\"name\":\"Zusatzangabe\"},\"id\":\"data_1\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_datastructure","summary":"Teilnehmer-Datenfeld oder Reihenfolge ändern","description":"Ändert die Kontokonfiguration eines Feldschlüssels oder sort. Der Selektor ist ein Standardfeld/data_1 bis data_30 oder sort, keine v2-ID. sort erwartet data.sort als Semikolon-Liste und ergänzt nicht genannte Felder hinten. Gesperrte Felder erlauben nur backend; duplicate=true ist nur bei duplikatfähigen Feldern zulässig. config.type=date/file setzt die Feldart, sonst wird config gelöscht. Das Abonnement muss die Funktion enthalten. Nach dem Speichern werden die Datenstruktur-Caches geleert.","tags":["datastructure"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.update=true bestätigt das Speichern und die Cache-Invalidierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"const":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"show":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"backend":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"name":{"description":"Auf Unicode-Buchstaben, Ziffern, Leerzeichen und Bindestrich beschränkt und auf 45 Zeichen normalisiert.","x-edoobox-canonicalType":"string"},"duplicate":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"config":{"description":"Objekt mit type=date/file; andere Werte löschen die Konfiguration.","examples":[{"type":"date"}]}},"additionalProperties":false},{"type":"object","properties":{"sort":{"type":"string"}},"additionalProperties":true,"required":["sort"]}]},"id":{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30","sort"],"examples":["data_1"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Teilnehmer-Datenfeld oder Reihenfolge ändern","value":{"data":{"show":true,"backend":true,"name":"Zusatzangabe"},"id":"data_1"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Datastructure.php","method":"updateDatastructure","line":163},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"datastructure","short":"dast","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/datastructure/{key} Teilnehmer-Datenfeld oder Reihenfolge ändern Ändert die Kontokonfiguration eines Feldschlüssels oder sort. Der Selektor ist ein Standardfeld/data_1 bis data_30 oder sort, keine v2-ID. sort erwartet data.sort als Semikolon-Liste und ergänzt nicht genannte Felder hinten. Gesperrte Felder erlauben nur backend; duplicate=true ist nur bei duplikatfähigen Feldern zulässig. config.type=date/file setzt die Feldart, sonst wird config gelöscht. Das Abonnement muss die Funktion enthalten. Nach dem Speichern werden die Datenstruktur-Caches geleert. - Operation-ID: put_v2_datastructure_key - Dokumentation: /docs/api/v2/operations/put-v2-datastructure-key-d33a3109e664/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | key | path | ja | Feldschlüssel oder sort. | | language | header | nein | | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"key","in":"path","required":true,"schema":{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30","sort"],"examples":["data_1"]},"description":"Feldschlüssel oder sort."},{"name":"language","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_KEY:?PATH_KEY setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "show": true, "backend": true, "name": "Zusatzangabe" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/datastructure/'"${PATH_KEY}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/datastructure/{key}"; path = path.replaceAll("{key}", encodeURIComponent(required("PATH_KEY"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "show": true, "backend": true, "name": "Zusatzangabe" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/datastructure/{key}" path = path.replace("{key}", urllib.parse.quote(os.environ["PATH_KEY"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"show\":true,\"backend\":true,\"name\":\"Zusatzangabe\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_datastructure_key","summary":"Teilnehmer-Datenfeld oder Reihenfolge ändern","description":"Ändert die Kontokonfiguration eines Feldschlüssels oder sort. Der Selektor ist ein Standardfeld/data_1 bis data_30 oder sort, keine v2-ID. sort erwartet data.sort als Semikolon-Liste und ergänzt nicht genannte Felder hinten. Gesperrte Felder erlauben nur backend; duplicate=true ist nur bei duplikatfähigen Feldern zulässig. config.type=date/file setzt die Feldart, sonst wird config gelöscht. Das Abonnement muss die Funktion enthalten. Nach dem Speichern werden die Datenstruktur-Caches geleert.","tags":["datastructure"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30","sort"],"examples":["data_1"]},"description":"Feldschlüssel oder sort."},{"name":"language","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.update=true bestätigt das Speichern und die Cache-Invalidierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"const":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"show":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"backend":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"name":{"description":"Auf Unicode-Buchstaben, Ziffern, Leerzeichen und Bindestrich beschränkt und auf 45 Zeichen normalisiert.","x-edoobox-canonicalType":"string"},"duplicate":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"config":{"description":"Objekt mit type=date/file; andere Werte löschen die Konfiguration.","examples":[{"type":"date"}]}},"additionalProperties":false},{"type":"object","properties":{"sort":{"type":"string"}},"additionalProperties":true,"required":["sort"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Teilnehmer-Datenfeld oder Reihenfolge ändern","value":{"data":{"show":true,"backend":true,"name":"Zusatzangabe"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Datastructure.php","method":"updateDatastructure","line":163},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"datastructure","short":"dast","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/date Termin löschen Löscht Termin im gewählten Konto. Eine öffentliche Ziel-ID ist erforderlich: als id im JSON-Umschlag oder als gleichnamiger Query-Parameter. Ein JSON-id hat Vorrang. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten. Zugeordnete Terminbuchungen verhindern die Löschung (ED80201 mit lesson_booking und booking_count). - Operation-ID: delete_v2_date - Dokumentation: /docs/api/v2/operations/delete-v2-date-4302ebcc70bd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "date_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/date' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "date_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"date_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_date","summary":"Termin löschen","description":"Löscht Termin im gewählten Konto. Eine öffentliche Ziel-ID ist erforderlich: als id im JSON-Umschlag oder als gleichnamiger Query-Parameter. Ein JSON-id hat Vorrang. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten. Zugeordnete Terminbuchungen verhindern die Löschung (ED80201 mit lesson_booking und booking_count).","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"date_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},"delete":{"type":"boolean"},"offer":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"date_start":{"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Termin löschen","value":{"id":"date_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"deleteDate","line":283},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/date Termine auflisten Liest Termine im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Termine gelöschter bzw. im Papierkorb befindlicher Angebote werden ausgeschlossen. Zusätzliche Filter: admin und user. Bei einem user-Filter wird die verknüpfte booking-ID zusätzlich ausgegeben. fields unterstützt offerdata, user_stats und calendar_entries; calendar_entries benötigt Leserechte für booking und user und enthält buchungsbezogene Kalenderdaten. - Operation-ID: get_v2_date - Dokumentation: /docs/api/v2/operations/get-v2-date-0bf37ca457eb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}},"example":["id","offer","date_start","date_end","offerdata","user_stats","calendar_entries"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/date' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_date","summary":"Termine auflisten","description":"Liest Termine im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Termine gelöschter bzw. im Papierkorb befindlicher Angebote werden ausgeschlossen. Zusätzliche Filter: admin und user. Bei einem user-Filter wird die verknüpfte booking-ID zusätzlich ausgegeben. fields unterstützt offerdata, user_stats und calendar_entries; calendar_entries benötigt Leserechte für booking und user und enthält buchungsbezogene Kalenderdaten.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}},"example":["id","offer","date_start","date_end","offerdata","user_stats","calendar_entries"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"date_012345abcdef_123456":{"id":"date_012345abcdef_123456","description":"Einführung","offer":"offer_012345abcdef_123456","date_start":"2026-11-03T09:00:00+01:00","date_end":"2026-11-03T10:00:00+01:00","place":false,"room":false,"leader":[],"color":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"string"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string"},{"const":false}]},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"leader":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}],"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter."},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offerdata":{"type":"object","properties":{"name":{"type":"string"},"number":{"type":"string"},"user_maximum":{"type":"integer"}},"additionalProperties":true},"user_stats":{"type":"object","properties":{"count_users":{"type":"integer"},"maximum_places":{"type":"integer"},"free_places":{"type":"integer"}},"additionalProperties":true},"calendar_entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booker_name":{"type":"string"},"participant_count":{"type":"integer"},"lesson_booking":{"type":"boolean"},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["questiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"key":{"type":"string"},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"title":{"type":"string"},"value":{"type":"string"},"response_type":{"type":"string"}},"additionalProperties":true,"required":["id","key","offer","title","value","response_type"]}}},"additionalProperties":true,"required":["id","booking","offer","booker_name","participant_count","lesson_booking","questions"]}}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"string"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string"},{"const":false}]},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"leader":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}],"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter."},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offerdata":{"type":"object","properties":{"name":{"type":"string"},"number":{"type":"string"},"user_maximum":{"type":"integer"}},"additionalProperties":true},"user_stats":{"type":"object","properties":{"count_users":{"type":"integer"},"maximum_places":{"type":"integer"},"free_places":{"type":"integer"}},"additionalProperties":true},"calendar_entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booker_name":{"type":"string"},"participant_count":{"type":"integer"},"lesson_booking":{"type":"boolean"},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["questiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"key":{"type":"string"},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"title":{"type":"string"},"value":{"type":"string"},"response_type":{"type":"string"}},"additionalProperties":true,"required":["id","key","offer","title","value","response_type"]}}},"additionalProperties":true,"required":["id","booking","offer","booker_name","participant_count","lesson_booking","questions"]}}},"additionalProperties":true}}],"description":"ID-indizierte Sammlung; response_type=array liefert eine Liste. Leere Ergebnismengen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"getDate","line":330},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/date Termin anlegen Legt Termin mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten. - Operation-ID: post_v2_date - Dokumentation: /docs/api/v2/operations/post-v2-date-0b4faa838f42/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "description": "Einführung", "date_start": "2026-11-03T09:00:00+01:00", "date_end": "2026-11-03T10:00:00+01:00", "leader": [] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/date' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "description": "Einführung", "date_start": "2026-11-03T09:00:00+01:00", "date_end": "2026-11-03T10:00:00+01:00", "leader": [] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"description\":\"Einführung\",\"date_start\":\"2026-11-03T09:00:00+01:00\",\"date_end\":\"2026-11-03T10:00:00+01:00\",\"leader\":[]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_date","summary":"Termin anlegen","description":"Legt Termin mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"date_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"},"offer":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"date_start":{"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":40},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":62},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"type":"null"},{"const":""},{"const":0},{"const":"0"}],"description":"A falsey value clears the relation; a nonempty public v2 ID selects the relation.","x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"type":"null"},{"const":""},{"const":0},{"const":"0"}],"description":"A falsey value clears the relation; a nonempty public v2 ID selects the relation.","x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":101},"leader":{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Public admin v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter.\n\nA JSON list of existing administrators in the selected account. The full deduplicated set is validated before assignment changes; [] removes all leaders. Wrong types, missing or foreign administrators reject the replacement.","x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"color":{"description":"Canonical #RGB/#RRGGBB. Also accepts unprefixed 4/7 hex chars; strings of other lengths clear. Length 4/7 nonhex rejects. Not equivalent to strict CSS hex regex. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nfalse or string length other than 4/7 -> empty aP string. At length4/7, hexadecimal after ltrim(all leading #) is required; nonhex returns false. Empty output -> false.\n\nRuntime preconditions / rejection: Length4/7 nonhex text returns strict false -> ED80100.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["#a1b",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["date_start","date_end","offer"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":40},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":62},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"type":"null"},{"const":""},{"const":0},{"const":"0"}],"description":"A falsey value clears the relation; a nonempty public v2 ID selects the relation.","x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"type":"null"},{"const":""},{"const":0},{"const":"0"}],"description":"A falsey value clears the relation; a nonempty public v2 ID selects the relation.","x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":101},"leader":{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Public admin v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter.\n\nA JSON list of existing administrators in the selected account. The full deduplicated set is validated before assignment changes; [] removes all leaders. Wrong types, missing or foreign administrators reject the replacement.","x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"color":{"description":"Canonical #RGB/#RRGGBB. Also accepts unprefixed 4/7 hex chars; strings of other lengths clear. Length 4/7 nonhex rejects. Not equivalent to strict CSS hex regex. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nfalse or string length other than 4/7 -> empty aP string. At length4/7, hexadecimal after ltrim(all leading #) is required; nonhex returns false. Empty output -> false.\n\nRuntime preconditions / rejection: Length4/7 nonhex text returns strict false -> ED80100.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["#a1b",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["date_start","date_end","offer"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Termin anlegen","value":{"data":{"offer":"offer_012345abcdef_123456","description":"Einführung","date_start":"2026-11-03T09:00:00+01:00","date_end":"2026-11-03T10:00:00+01:00","leader":[]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"postDate","line":189},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/date Termin aktualisieren Ändert die übergebenen schreibbaren Felder in data für Termin. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten. - Operation-ID: put_v2_date - Dokumentation: /docs/api/v2/operations/put-v2-date-ad3b2fdf7012/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": "Example" }, "id": "date_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/date' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": "Example" }, "id": "date_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":\"Example\"},\"id\":\"date_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_date","summary":"Termin aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Termin. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"date_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},"update":{"type":"boolean"},"offer":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"date_start":{"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":40},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":62},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"type":"null"},{"const":""},{"const":0},{"const":"0"}],"description":"A falsey value clears the relation; a nonempty public v2 ID selects the relation.","x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"type":"null"},{"const":""},{"const":0},{"const":"0"}],"description":"A falsey value clears the relation; a nonempty public v2 ID selects the relation.","x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":false,"leader":{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Public admin v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter.\n\nA JSON list of existing administrators in the selected account. The full deduplicated set is validated before assignment changes; [] removes all leaders. Wrong types, missing or foreign administrators reject the replacement.","x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"color":{"description":"Canonical #RGB/#RRGGBB. Also accepts unprefixed 4/7 hex chars; strings of other lengths clear. Length 4/7 nonhex rejects. Not equivalent to strict CSS hex regex. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nfalse or string length other than 4/7 -> empty aP string. At length4/7, hexadecimal after ltrim(all leading #) is required; nonhex returns false. Empty output -> false.\n\nRuntime preconditions / rejection: Length4/7 nonhex text returns strict false -> ED80100.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["#a1b",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["offer"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Termin aktualisieren","value":{"data":{"description":"Example"},"id":"date_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"updateDate","line":237},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/date/{id} Termin löschen Löscht Termin im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten. Zugeordnete Terminbuchungen verhindern die Löschung (ED80201 mit lesson_booking und booking_count). - Operation-ID: delete_v2_date_id - Dokumentation: /docs/api/v2/operations/delete-v2-date-id-902d1db52cb0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "date_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/date/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "date_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"date_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_date_id","summary":"Termin löschen","description":"Löscht Termin im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten. Zugeordnete Terminbuchungen verhindern die Löschung (ED80201 mit lesson_booking und booking_count).","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"date_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},"delete":{"type":"boolean"},"offer":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"date_start":{"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Termin löschen","value":{"id":"date_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"deleteDate","line":283},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/date/{id} Termin abrufen Liest Termin anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Termine gelöschter bzw. im Papierkorb befindlicher Angebote werden ausgeschlossen. Zusätzliche Filter: admin und user. Bei einem user-Filter wird die verknüpfte booking-ID zusätzlich ausgegeben. fields unterstützt offerdata, user_stats und calendar_entries; calendar_entries benötigt Leserechte für booking und user und enthält buchungsbezogene Kalenderdaten. - Operation-ID: get_v2_date_id - Dokumentation: /docs/api/v2/operations/get-v2-date-id-b8fa5f28b3bf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}},"example":["id","offer","date_start","date_end","offerdata","user_stats","calendar_entries"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/date/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_date_id","summary":"Termin abrufen","description":"Liest Termin anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Termine gelöschter bzw. im Papierkorb befindlicher Angebote werden ausgeschlossen. Zusätzliche Filter: admin und user. Bei einem user-Filter wird die verknüpfte booking-ID zusätzlich ausgegeben. fields unterstützt offerdata, user_stats und calendar_entries; calendar_entries benötigt Leserechte für booking und user und enthält buchungsbezogene Kalenderdaten.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}},"example":["id","offer","date_start","date_end","offerdata","user_stats","calendar_entries"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"date_012345abcdef_123456":{"id":"date_012345abcdef_123456","description":"Einführung","offer":"offer_012345abcdef_123456","date_start":"2026-11-03T09:00:00+01:00","date_end":"2026-11-03T10:00:00+01:00","place":false,"room":false,"leader":[],"color":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"string"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string"},{"const":false}]},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"leader":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}],"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter."},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offerdata":{"type":"object","properties":{"name":{"type":"string"},"number":{"type":"string"},"user_maximum":{"type":"integer"}},"additionalProperties":true},"user_stats":{"type":"object","properties":{"count_users":{"type":"integer"},"maximum_places":{"type":"integer"},"free_places":{"type":"integer"}},"additionalProperties":true},"calendar_entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booker_name":{"type":"string"},"participant_count":{"type":"integer"},"lesson_booking":{"type":"boolean"},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["questiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"key":{"type":"string"},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"title":{"type":"string"},"value":{"type":"string"},"response_type":{"type":"string"}},"additionalProperties":true,"required":["id","key","offer","title","value","response_type"]}}},"additionalProperties":true,"required":["id","booking","offer","booker_name","participant_count","lesson_booking","questions"]}}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"string"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string"},{"const":false}]},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"leader":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}],"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter."},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offerdata":{"type":"object","properties":{"name":{"type":"string"},"number":{"type":"string"},"user_maximum":{"type":"integer"}},"additionalProperties":true},"user_stats":{"type":"object","properties":{"count_users":{"type":"integer"},"maximum_places":{"type":"integer"},"free_places":{"type":"integer"}},"additionalProperties":true},"calendar_entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booker_name":{"type":"string"},"participant_count":{"type":"integer"},"lesson_booking":{"type":"boolean"},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["questiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"key":{"type":"string"},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"title":{"type":"string"},"value":{"type":"string"},"response_type":{"type":"string"}},"additionalProperties":true,"required":["id","key","offer","title","value","response_type"]}}},"additionalProperties":true,"required":["id","booking","offer","booker_name","participant_count","lesson_booking","questions"]}}},"additionalProperties":true}}],"description":"ID-indizierte Sammlung; response_type=array liefert eine Liste. Leere Ergebnismengen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"getDate","line":330},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/date/{id} Termin aktualisieren Ändert die übergebenen schreibbaren Felder in data für Termin. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten. - Operation-ID: put_v2_date_id - Dokumentation: /docs/api/v2/operations/put-v2-date-id-0e171d7851d8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/date/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_date_id","summary":"Termin aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Termin. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. Nach erfolgreicher Änderung werden zusammengefasste Angebotsdaten (erster/letzter Termin und Ort) neu berechnet; data.offer kann die dabei ermittelten Angebotsdaten enthalten.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"date_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},"update":{"type":"boolean"},"offer":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"date_start":{"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":40},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":62},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"type":"null"},{"const":""},{"const":0},{"const":"0"}],"description":"A falsey value clears the relation; a nonempty public v2 ID selects the relation.","x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"type":"null"},{"const":""},{"const":0},{"const":"0"}],"description":"A falsey value clears the relation; a nonempty public v2 ID selects the relation.","x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":false,"leader":{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Public admin v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter.\n\nA JSON list of existing administrators in the selected account. The full deduplicated set is validated before assignment changes; [] removes all leaders. Wrong types, missing or foreign administrators reject the replacement.","x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"color":{"description":"Canonical #RGB/#RRGGBB. Also accepts unprefixed 4/7 hex chars; strings of other lengths clear. Length 4/7 nonhex rejects. Not equivalent to strict CSS hex regex. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nfalse or string length other than 4/7 -> empty aP string. At length4/7, hexadecimal after ltrim(all leading #) is required; nonhex returns false. Empty output -> false.\n\nRuntime preconditions / rejection: Length4/7 nonhex text returns strict false -> ED80100.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["#a1b",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["offer"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"description":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"updateDate","line":237},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/date/{id}/dashboard Terminübersicht mit Belegungszahlen abrufen Liefert den Termin, das zugehörige Angebot, Tags und Belegungszahlen. Bei freier Terminauswahl werden die Teilnehmer dieses Termins gezählt; ansonsten gelten die Teilnehmer des Angebots. Ein gelöschtes oder im Papierkorb befindliches Angebot wird zurückgewiesen. fields und Listenpaginierung ändern den Dashboardumfang nicht. - Operation-ID: get_v2_date_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-date-id-dashboard-2c16f748ea29/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/date/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_date_id_dashboard","summary":"Terminübersicht mit Belegungszahlen abrufen","description":"Liefert den Termin, das zugehörige Angebot, Tags und Belegungszahlen. Bei freier Terminauswahl werden die Teilnehmer dieses Termins gezählt; ansonsten gelten die Teilnehmer des Angebots. Ein gelöschtes oder im Papierkorb befindliches Angebot wird zurückgewiesen. fields und Listenpaginierung ändern den Dashboardumfang nicht.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"date_id":"date_012345abcdef_123456","date":{"id":"date_012345abcdef_123456","offer":"offer_012345abcdef_123456","description":"Einführung"},"offer":{"id":"offer_012345abcdef_123456","name":"Beispielangebot","user_maximum":20},"tags":[],"user_stats":{"count_users":8,"maximum_places":20,"free_places":12}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"date_id":{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},"date":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"string"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string"},{"const":false}]},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"leader":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}],"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter."},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true},"offer":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true},"tags":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"user_stats":{"type":"object","properties":{"count_users":{"type":"integer"},"free_places":{"type":"integer"},"maximum_places":{"type":"integer"}},"additionalProperties":true,"required":["count_users","free_places","maximum_places"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/dashboard/Datedashboard.php","line":10},"reason":"Complete reachable dashboard graph reviewed: SELECT aggregations, existing links, request-local formatting and serializers. Nested Eddata reads clear action segments and do not reach tree repair or other mutation branches. Derived read caches are not domain changes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"getDate","line":330,"branch":{"file":"application/models/v2/Date.php","line":337}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/date/feed/ical_feed URL für einen iCalendar-Feed erzeugen Erzeugt eine verschlüsselte Feed-URL für das Konto. data.admin muss eine aktive kontoeigene Administrator-ID sein, auch bei globaler Zuordnung. assigned=admin begrenzt den Feed auf diese Leitung; andere bzw. fehlende Werte ergeben global. Es werden keine Termine angelegt und kein Feed-Datensatz gespeichert. - Operation-ID: post_v2_date_feed_ical_feed - Dokumentation: /docs/api/v2/operations/post-v2-date-feed-ical-feed-008028acd1be/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "admin": "admin_012345abcdef_123456", "assigned": "admin" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/date/feed/ical_feed' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date/feed/ical_feed"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "admin": "admin_012345abcdef_123456", "assigned": "admin" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date/feed/ical_feed" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"admin\":\"admin_012345abcdef_123456\",\"assigned\":\"admin\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_date_feed_ical_feed","summary":"URL für einen iCalendar-Feed erzeugen","description":"Erzeugt eine verschlüsselte Feed-URL für das Konto. data.admin muss eine aktive kontoeigene Administrator-ID sein, auch bei globaler Zuordnung. assigned=admin begrenzt den Feed auf diese Leitung; andere bzw. fehlende Werte ergeben global. Es werden keine Termine angelegt und kein Feed-Datensatz gespeichert.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"url":"https://app.edoobox.com/v2/auth/i/c.ics?t=synthetischer-token&n=beispiel"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string"}},"additionalProperties":true,"required":["url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]},"assigned":{"description":"admin für die gewählte Leitung; andere Werte und fehlende Angabe ergeben global. Keine strikte Typprüfung für abweichende Werte.","examples":["admin","global"],"x-edoobox-canonicalType":"string"}},"additionalProperties":true,"required":["admin"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"URL für einen iCalendar-Feed erzeugen","value":{"data":{"admin":"admin_012345abcdef_123456","assigned":"admin"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Date.php","line":190,"reason":"segment4 selects feed action; segment3 is ignored by Datefunc::_setFublicFeedURL; Vue sends literal feed."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"postDate","line":189,"branch":{"file":"application/models/v2/Date.php","line":195}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/date/list Termine auflisten Liest Termine im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Termine gelöschter bzw. im Papierkorb befindlicher Angebote werden ausgeschlossen. Zusätzliche Filter: admin und user. Bei einem user-Filter wird die verknüpfte booking-ID zusätzlich ausgegeben. fields unterstützt offerdata, user_stats und calendar_entries; calendar_entries benötigt Leserechte für booking und user und enthält buchungsbezogene Kalenderdaten. - Operation-ID: get_v2_date_list - Dokumentation: /docs/api/v2/operations/get-v2-date-list-8ed1ba9721ab/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}},"example":["id","offer","date_start","date_end","offerdata","user_stats","calendar_entries"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/date/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_date_list","summary":"Termine auflisten","description":"Liest Termine im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Termine gelöschter bzw. im Papierkorb befindlicher Angebote werden ausgeschlossen. Zusätzliche Filter: admin und user. Bei einem user-Filter wird die verknüpfte booking-ID zusätzlich ausgegeben. fields unterstützt offerdata, user_stats und calendar_entries; calendar_entries benötigt Leserechte für booking und user und enthält buchungsbezogene Kalenderdaten.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"Felder der Termine. offerdata ergänzt Angebotsname, Nummer und maximale Teilnehmerzahl; user_stats ergänzt Belegung. calendar_entries enthält buchungsbezogene Kalenderkarten und Antworten und benötigt Leserechte für booking und user.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}},"example":["id","offer","date_start","date_end","offerdata","user_stats","calendar_entries"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"date_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"date_012345abcdef_123456":{"id":"date_012345abcdef_123456","description":"Einführung","offer":"offer_012345abcdef_123456","date_start":"2026-11-03T09:00:00+01:00","date_end":"2026-11-03T10:00:00+01:00","place":false,"room":false,"leader":[],"color":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"string"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string"},{"const":false}]},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"leader":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}],"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter."},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offerdata":{"type":"object","properties":{"name":{"type":"string"},"number":{"type":"string"},"user_maximum":{"type":"integer"}},"additionalProperties":true},"user_stats":{"type":"object","properties":{"count_users":{"type":"integer"},"maximum_places":{"type":"integer"},"free_places":{"type":"integer"}},"additionalProperties":true},"calendar_entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booker_name":{"type":"string"},"participant_count":{"type":"integer"},"lesson_booking":{"type":"boolean"},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["questiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"key":{"type":"string"},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"title":{"type":"string"},"value":{"type":"string"},"response_type":{"type":"string"}},"additionalProperties":true,"required":["id","key","offer","title","value","response_type"]}}},"additionalProperties":true,"required":["id","booking","offer","booker_name","participant_count","lesson_booking","questions"]}}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"string"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string"},{"const":false}]},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"leader":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}],"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter."},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offerdata":{"type":"object","properties":{"name":{"type":"string"},"number":{"type":"string"},"user_maximum":{"type":"integer"}},"additionalProperties":true},"user_stats":{"type":"object","properties":{"count_users":{"type":"integer"},"maximum_places":{"type":"integer"},"free_places":{"type":"integer"}},"additionalProperties":true},"calendar_entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"booker_name":{"type":"string"},"participant_count":{"type":"integer"},"lesson_booking":{"type":"boolean"},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","examples":["questiongroup_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"key":{"type":"string"},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"title":{"type":"string"},"value":{"type":"string"},"response_type":{"type":"string"}},"additionalProperties":true,"required":["id","key","offer","title","value","response_type"]}}},"additionalProperties":true,"required":["id","booking","offer","booker_name","participant_count","lesson_booking","questions"]}}},"additionalProperties":true}}],"description":"ID-indizierte Sammlung; response_type=array liefert eine Liste. Leere Ergebnismengen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"getDate","line":330},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/date/list/ical_feed URL für einen iCalendar-Feed erzeugen Erzeugt eine verschlüsselte Feed-URL für das Konto. data.admin muss eine aktive kontoeigene Administrator-ID sein, auch bei globaler Zuordnung. assigned=admin begrenzt den Feed auf diese Leitung; andere bzw. fehlende Werte ergeben global. Es werden keine Termine angelegt und kein Feed-Datensatz gespeichert. - Operation-ID: post_v2_date_list_ical_feed - Dokumentation: /docs/api/v2/operations/post-v2-date-list-ical-feed-41c609c55337/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "admin": "admin_012345abcdef_123456", "assigned": "admin" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/date/list/ical_feed' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/date/list/ical_feed"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "admin": "admin_012345abcdef_123456", "assigned": "admin" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/date/list/ical_feed" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"admin\":\"admin_012345abcdef_123456\",\"assigned\":\"admin\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_date_list_ical_feed","summary":"URL für einen iCalendar-Feed erzeugen","description":"Erzeugt eine verschlüsselte Feed-URL für das Konto. data.admin muss eine aktive kontoeigene Administrator-ID sein, auch bei globaler Zuordnung. assigned=admin begrenzt den Feed auf diese Leitung; andere bzw. fehlende Werte ergeben global. Es werden keine Termine angelegt und kein Feed-Datensatz gespeichert.","tags":["date"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"url":"https://app.edoobox.com/v2/auth/i/c.ics?t=synthetischer-token&n=beispiel"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string"}},"additionalProperties":true,"required":["url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]},"assigned":{"description":"admin für die gewählte Leitung; andere Werte und fehlende Angabe ergeben global. Keine strikte Typprüfung für abweichende Werte.","examples":["admin","global"],"x-edoobox-canonicalType":"string"}},"additionalProperties":true,"required":["admin"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"URL für einen iCalendar-Feed erzeugen","value":{"data":{"admin":"admin_012345abcdef_123456","assigned":"admin"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Date.php","method":"postDate","line":189,"branch":{"file":"application/models/v2/Date.php","line":195}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"date","short":"date","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/deliverables Zusatzleistung auflisten Liest Zusatzleistung-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein. - Operation-ID: get_v2_deliverables - Dokumentation: /docs/api/v2/operations/get-v2-deliverables-663bca705147/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/deliverables' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverables"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverables" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_deliverables","summary":"Zusatzleistung auflisten","description":"Liest Zusatzleistung-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein.","tags":["deliverables"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"deliverables_012345abcdef_123456":{"id":"deliverables_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},"status":{"type":"boolean"},"deliverablesgroup":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"name":{"type":"string"},"description":{"type":"string"},"settlement":{"type":"string","enum":["add","subtract"]},"amount":{"type":"number"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},"status":{"type":"boolean"},"deliverablesgroup":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"name":{"type":"string"},"description":{"type":"string"},"settlement":{"type":"string","enum":["add","subtract"]},"amount":{"type":"number"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverables.php","method":"getDeliverables","line":230},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"deliverables","short":"dela","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/deliverables Zusatzleistung erstellen Erstellt Zusatzleistung aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft. - Operation-ID: post_v2_deliverables - Dokumentation: /docs/api/v2/operations/post-v2-deliverables-338b697ad73c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/deliverables' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverables"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = readFileSync("payload.json"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/deliverables" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = Path("payload.json").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_deliverables","summary":"Zusatzleistung erstellen","description":"Erstellt Zusatzleistung aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft.","tags":["deliverables"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält eine öffentliche ID oder bei Stapelerstellung eine ID-Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"deliverables_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":31,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"deliverablesgroup":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":40},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":54},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":81},"settlement":{"type":"string","enum":["add","subtract"],"description":"add erhöht, subtract verringert den Preis; sub ist kein gültiger Schreibwert."},"amount":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":128,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":145,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["deliverablesgroup","name","settlement","amount"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":31,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"deliverablesgroup":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":40},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":54},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":81},"settlement":{"type":"string","enum":["add","subtract"],"description":"add erhöht, subtract verringert den Preis; sub ist kein gültiger Schreibwert."},"amount":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":128,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":145,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["deliverablesgroup","name","settlement","amount"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverables_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverables.php","method":"postDeliverables","line":180},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"deliverables","short":"dela","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/deliverables Zusatzleistung ändern Ändert die übergebenen veränderbaren Felder von Zusatzleistung. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. - Operation-ID: put_v2_deliverables - Dokumentation: /docs/api/v2/operations/put-v2-deliverables-b4ab23725c68/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" }, "id": "deliverables_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/deliverables' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverables"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" }, "id": "deliverables_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverables" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"},\"id\":\"deliverables_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_deliverables","summary":"Zusatzleistung ändern","description":"Ändert die übergebenen veränderbaren Felder von Zusatzleistung. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert.","tags":["deliverables"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"deliverables_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":31,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"deliverablesgroup":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":54},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":81},"settlement":{"type":"string","enum":["add","subtract"],"description":"add erhöht, subtract verringert den Preis; sub ist kein gültiger Schreibwert."},"amount":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":128,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":145,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["deliverablesgroup"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverables_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Zusatzleistung ändern","value":{"data":{"name":"Example"},"id":"deliverables_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverables.php","method":"updateDeliverables","line":197},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"deliverables","short":"dela","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/deliverables/{id} Zusatzleistung lesen Liest Zusatzleistung anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein. - Operation-ID: get_v2_deliverables_id - Dokumentation: /docs/api/v2/operations/get-v2-deliverables-id-19cd2bb2abff/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverables_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/deliverables/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverables/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverables/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_deliverables_id","summary":"Zusatzleistung lesen","description":"Liest Zusatzleistung anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein.","tags":["deliverables"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverables_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"deliverables_012345abcdef_123456":{"id":"deliverables_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},"status":{"type":"boolean"},"deliverablesgroup":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"name":{"type":"string"},"description":{"type":"string"},"settlement":{"type":"string","enum":["add","subtract"]},"amount":{"type":"number"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},"status":{"type":"boolean"},"deliverablesgroup":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"name":{"type":"string"},"description":{"type":"string"},"settlement":{"type":"string","enum":["add","subtract"]},"amount":{"type":"number"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverables.php","method":"getDeliverables","line":230},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"deliverables","short":"dela","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/deliverables/{id} Zusatzleistung ändern Ändert die übergebenen veränderbaren Felder von Zusatzleistung. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. - Operation-ID: put_v2_deliverables_id - Dokumentation: /docs/api/v2/operations/put-v2-deliverables-id-c4fa8b6e2598/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverables_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/deliverables/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverables/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverables/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_deliverables_id","summary":"Zusatzleistung ändern","description":"Ändert die übergebenen veränderbaren Felder von Zusatzleistung. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert.","tags":["deliverables"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverables_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"deliverables_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":31,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"deliverablesgroup":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":54},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":81},"settlement":{"type":"string","enum":["add","subtract"],"description":"add erhöht, subtract verringert den Preis; sub ist kein gültiger Schreibwert."},"amount":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":128,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":145,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["deliverablesgroup"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverables_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zusatzleistung ändern","value":{"data":{"name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverables.php","method":"updateDeliverables","line":197},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"deliverables","short":"dela","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/deliverables/list Zusatzleistung auflisten Liest Zusatzleistung-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein. - Operation-ID: get_v2_deliverables_list - Dokumentation: /docs/api/v2/operations/get-v2-deliverables-list-e0325125d0de/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/deliverables/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverables/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverables/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_deliverables_list","summary":"Zusatzleistung auflisten","description":"Liest Zusatzleistung-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein.","tags":["deliverables"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverables_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"deliverables_012345abcdef_123456":{"id":"deliverables_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},"status":{"type":"boolean"},"deliverablesgroup":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"name":{"type":"string"},"description":{"type":"string"},"settlement":{"type":"string","enum":["add","subtract"]},"amount":{"type":"number"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverables_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverables_012345abcdef_123456"]},"status":{"type":"boolean"},"deliverablesgroup":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"name":{"type":"string"},"description":{"type":"string"},"settlement":{"type":"string","enum":["add","subtract"]},"amount":{"type":"number"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverables.php","method":"getDeliverables","line":230},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"deliverables","short":"dela","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/deliverablesgroup Leistungsgruppe löschen Löscht Leistungsgruppe, indem der Datensatz als gelöscht markiert wird. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang. - Operation-ID: delete_v2_deliverablesgroup - Dokumentation: /docs/api/v2/operations/delete-v2-deliverablesgroup-d28e5c938dbd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "deliverablesgroup_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/deliverablesgroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverablesgroup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "deliverablesgroup_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverablesgroup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"deliverablesgroup_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_deliverablesgroup","summary":"Leistungsgruppe löschen","description":"Löscht Leistungsgruppe, indem der Datensatz als gelöscht markiert wird. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang.","tags":["deliverablesgroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id ist die öffentliche ID; delete zeigt eine betroffene Zeile an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"deliverablesgroup_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Leistungsgruppe löschen","value":{"id":"deliverablesgroup_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverablesgroup.php","method":"deleteDeliverablesgroup","line":190},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"deliverablesgroup","short":"delg","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/deliverablesgroup Leistungsgruppe auflisten Liest Leistungsgruppe-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_deliverablesgroup - Dokumentation: /docs/api/v2/operations/get-v2-deliverablesgroup-ac3373fa7bc9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/deliverablesgroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverablesgroup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverablesgroup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_deliverablesgroup","summary":"Leistungsgruppe auflisten","description":"Liest Leistungsgruppe-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["deliverablesgroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"deliverablesgroup_012345abcdef_123456":{"id":"deliverablesgroup_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"country":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["vat_012345abcdef_123456"]},{"const":false}]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"country":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["vat_012345abcdef_123456"]},{"const":false}]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverablesgroup.php","method":"getDeliverablesgroup","line":206},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"deliverablesgroup","short":"delg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/deliverablesgroup Leistungsgruppe erstellen Erstellt Leistungsgruppe aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft. Ein gesetzter vat muss zum Land der Leistungsgruppe gehören; vat=false entfernt die Zuordnung bei PUT. - Operation-ID: post_v2_deliverablesgroup - Dokumentation: /docs/api/v2/operations/post-v2-deliverablesgroup-4cb21c97c63a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "title": "Example", "country": "country_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/deliverablesgroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverablesgroup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "title": "Example", "country": "country_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverablesgroup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"title\":\"Example\",\"country\":\"country_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_deliverablesgroup","summary":"Leistungsgruppe erstellen","description":"Erstellt Leistungsgruppe aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft. Ein gesetzter vat muss zum Land der Leistungsgruppe gehören; vat=false entfernt die Zuordnung bei PUT.","tags":["deliverablesgroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält eine öffentliche ID oder bei Stapelerstellung eine ID-Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"deliverablesgroup_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":27,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"title":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":45},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":73},"vat":{"description":"Use account-valid VAT v2 ID or false. POST loose-false and invalid ID decode can normalize to no VAT; nonzero relation must match account/country.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-supportedWireSchema":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 VAT ID returned in selected account; account hash, existence and country require semantic validation."},{"const":false}]},"examples":["vat_012345abcdef_123456",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Deliverablesgroup.php","line":96,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"63163740b8a84a829b2d0c53f93bded5918152f7e782daae7edf398041b09f43"},{"file":"application/models/v2/Deliverablesgroup.php","line":157,"reason":"POST/PUT caller chooses Deliverablesgroup hooks.","sha256":"63163740b8a84a829b2d0c53f93bded5918152f7e782daae7edf398041b09f43"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":16,"reason":"Before-insert checks only nonzero converted VAT.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":29,"reason":"Actual update hook: decoded valid relation or strict false; otherwise false.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":61,"reason":"Account-scoped VAT lookup and country equality.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/helpers/edooboxhelp_helper.php","line":11523,"reason":"edisset loose truthiness gate.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/libraries/edoobox/helper/Api.php","line":65,"reason":"Public-ID decoder checks string/resource/account hash, invalid returns 0.","sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":112}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["title","country"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":27,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"title":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":45},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":73},"vat":{"description":"Use account-valid VAT v2 ID or false. POST loose-false and invalid ID decode can normalize to no VAT; nonzero relation must match account/country.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-supportedWireSchema":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 VAT ID returned in selected account; account hash, existence and country require semantic validation."},{"const":false}]},"examples":["vat_012345abcdef_123456",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Deliverablesgroup.php","line":96,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"63163740b8a84a829b2d0c53f93bded5918152f7e782daae7edf398041b09f43"},{"file":"application/models/v2/Deliverablesgroup.php","line":157,"reason":"POST/PUT caller chooses Deliverablesgroup hooks.","sha256":"63163740b8a84a829b2d0c53f93bded5918152f7e782daae7edf398041b09f43"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":16,"reason":"Before-insert checks only nonzero converted VAT.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":29,"reason":"Actual update hook: decoded valid relation or strict false; otherwise false.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":61,"reason":"Account-scoped VAT lookup and country equality.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/helpers/edooboxhelp_helper.php","line":11523,"reason":"edisset loose truthiness gate.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/libraries/edoobox/helper/Api.php","line":65,"reason":"Public-ID decoder checks string/resource/account hash, invalid returns 0.","sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":112}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["title","country"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"title":"Example","country":"country_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverablesgroup.php","method":"postDeliverablesgroup","line":157},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"deliverablesgroup","short":"delg","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/deliverablesgroup Leistungsgruppe ändern Ändert die übergebenen veränderbaren Felder von Leistungsgruppe. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. Ein gesetzter vat muss zum Land der Leistungsgruppe gehören; vat=false entfernt die Zuordnung bei PUT. - Operation-ID: put_v2_deliverablesgroup - Dokumentation: /docs/api/v2/operations/put-v2-deliverablesgroup-2c6e98ad9168/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "title": "Example" }, "id": "deliverablesgroup_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/deliverablesgroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverablesgroup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "title": "Example" }, "id": "deliverablesgroup_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverablesgroup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"title\":\"Example\"},\"id\":\"deliverablesgroup_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_deliverablesgroup","summary":"Leistungsgruppe ändern","description":"Ändert die übergebenen veränderbaren Felder von Leistungsgruppe. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. Ein gesetzter vat muss zum Land der Leistungsgruppe gehören; vat=false entfernt die Zuordnung bei PUT.","tags":["deliverablesgroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"deliverablesgroup_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":27,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"title":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":45},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":73},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 VAT ID returned in selected account; account hash, existence and country require semantic validation."},{"const":false}],"description":"Set account-valid country-compatible VAT, or remove with literal false. Empty/null/0 and malformed IDs do not remove on PUT.","x-edoobox-schemaStatus":"source-reviewed","examples":["vat_012345abcdef_123456",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Deliverablesgroup.php","line":96,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"63163740b8a84a829b2d0c53f93bded5918152f7e782daae7edf398041b09f43"},{"file":"application/models/v2/Deliverablesgroup.php","line":157,"reason":"POST/PUT caller chooses Deliverablesgroup hooks.","sha256":"63163740b8a84a829b2d0c53f93bded5918152f7e782daae7edf398041b09f43"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":16,"reason":"Before-insert checks only nonzero converted VAT.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":29,"reason":"Actual update hook: decoded valid relation or strict false; otherwise false.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":61,"reason":"Account-scoped VAT lookup and country equality.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/helpers/edooboxhelp_helper.php","line":11523,"reason":"edisset loose truthiness gate.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/libraries/edoobox/helper/Api.php","line":65,"reason":"Public-ID decoder checks string/resource/account hash, invalid returns 0.","sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":112}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Leistungsgruppe ändern","value":{"data":{"title":"Example"},"id":"deliverablesgroup_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverablesgroup.php","method":"updateDeliverablesgroup","line":173},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"deliverablesgroup","short":"delg","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/deliverablesgroup/{id} Leistungsgruppe löschen Löscht Leistungsgruppe, indem der Datensatz als gelöscht markiert wird. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang. - Operation-ID: delete_v2_deliverablesgroup_id - Dokumentation: /docs/api/v2/operations/delete-v2-deliverablesgroup-id-c1f89998d211/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "deliverablesgroup_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/deliverablesgroup/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverablesgroup/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "deliverablesgroup_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverablesgroup/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"deliverablesgroup_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_deliverablesgroup_id","summary":"Leistungsgruppe löschen","description":"Löscht Leistungsgruppe, indem der Datensatz als gelöscht markiert wird. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang.","tags":["deliverablesgroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id ist die öffentliche ID; delete zeigt eine betroffene Zeile an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"deliverablesgroup_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Leistungsgruppe löschen","value":{"id":"deliverablesgroup_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverablesgroup.php","method":"deleteDeliverablesgroup","line":190},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"deliverablesgroup","short":"delg","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/deliverablesgroup/{id} Leistungsgruppe lesen Liest Leistungsgruppe anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_deliverablesgroup_id - Dokumentation: /docs/api/v2/operations/get-v2-deliverablesgroup-id-2e53b3d2d2d4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/deliverablesgroup/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverablesgroup/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverablesgroup/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_deliverablesgroup_id","summary":"Leistungsgruppe lesen","description":"Liest Leistungsgruppe anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["deliverablesgroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"deliverablesgroup_012345abcdef_123456":{"id":"deliverablesgroup_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"country":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["vat_012345abcdef_123456"]},{"const":false}]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"country":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["vat_012345abcdef_123456"]},{"const":false}]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverablesgroup.php","method":"getDeliverablesgroup","line":206},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"deliverablesgroup","short":"delg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/deliverablesgroup/{id} Leistungsgruppe ändern Ändert die übergebenen veränderbaren Felder von Leistungsgruppe. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. Ein gesetzter vat muss zum Land der Leistungsgruppe gehören; vat=false entfernt die Zuordnung bei PUT. - Operation-ID: put_v2_deliverablesgroup_id - Dokumentation: /docs/api/v2/operations/put-v2-deliverablesgroup-id-1adada061350/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "title": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/deliverablesgroup/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverablesgroup/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "title": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverablesgroup/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"title\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_deliverablesgroup_id","summary":"Leistungsgruppe ändern","description":"Ändert die übergebenen veränderbaren Felder von Leistungsgruppe. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. Ein gesetzter vat muss zum Land der Leistungsgruppe gehören; vat=false entfernt die Zuordnung bei PUT.","tags":["deliverablesgroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"deliverablesgroup_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":27,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"title":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":45},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":73},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 VAT ID returned in selected account; account hash, existence and country require semantic validation."},{"const":false}],"description":"Set account-valid country-compatible VAT, or remove with literal false. Empty/null/0 and malformed IDs do not remove on PUT.","x-edoobox-schemaStatus":"source-reviewed","examples":["vat_012345abcdef_123456",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Deliverablesgroup.php","line":96,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"63163740b8a84a829b2d0c53f93bded5918152f7e782daae7edf398041b09f43"},{"file":"application/models/v2/Deliverablesgroup.php","line":157,"reason":"POST/PUT caller chooses Deliverablesgroup hooks.","sha256":"63163740b8a84a829b2d0c53f93bded5918152f7e782daae7edf398041b09f43"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":16,"reason":"Before-insert checks only nonzero converted VAT.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":29,"reason":"Actual update hook: decoded valid relation or strict false; otherwise false.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/models/v2/Deliverablesgroupfunc.php","line":61,"reason":"Account-scoped VAT lookup and country equality.","sha256":"736c57093148ddaeab409b52f19de7f10a5f66ee71cadb06ca6c5b107643b860"},{"file":"application/helpers/edooboxhelp_helper.php","line":11523,"reason":"edisset loose truthiness gate.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/libraries/edoobox/helper/Api.php","line":65,"reason":"Public-ID decoder checks string/resource/account hash, invalid returns 0.","sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":112}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["deliverablesgroup_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Leistungsgruppe ändern","value":{"data":{"title":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverablesgroup.php","method":"updateDeliverablesgroup","line":173},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"deliverablesgroup","short":"delg","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/deliverablesgroup/list Leistungsgruppe auflisten Liest Leistungsgruppe-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_deliverablesgroup_list - Dokumentation: /docs/api/v2/operations/get-v2-deliverablesgroup-list-1824045fd959/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/deliverablesgroup/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/deliverablesgroup/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/deliverablesgroup/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_deliverablesgroup_list","summary":"Leistungsgruppe auflisten","description":"Liest Leistungsgruppe-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["deliverablesgroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"deliverablesgroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"deliverablesgroup_012345abcdef_123456":{"id":"deliverablesgroup_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"country":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["vat_012345abcdef_123456"]},{"const":false}]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"country":{"anyOf":[{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["country_012345abcdef_123456"]},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["vat_012345abcdef_123456"]},{"const":false}]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Deliverablesgroup.php","method":"getDeliverablesgroup","line":206},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"deliverablesgroup","short":"delg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/design Buchungsdesign löschen Löscht ein eigenes Buchungsdesign und dessen Widgets. Die Löschlogik setzt betroffene Designreferenzen zurück und ersetzt ein gelöschtes Standarddesign durch die Systemvorlage. Die Datenbankänderungen werden gemeinsam transaktional ausgeführt. - Operation-ID: delete_v2_design - Dokumentation: /docs/api/v2/operations/delete-v2-design-5069f86832c6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | ja | Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":true,"schema":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]},"description":"Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_ID:?QUERY_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "design_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/design' \ --url-query 'id='"${QUERY_ID}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("id", required("QUERY_ID")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "design_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("id", os.environ["QUERY_ID"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"design_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_design","summary":"Buchungsdesign löschen","description":"Löscht ein eigenes Buchungsdesign und dessen Widgets. Die Löschlogik setzt betroffene Designreferenzen zurück und ersetzt ein gelöschtes Standarddesign durch die Systemvorlage. Die Datenbankänderungen werden gemeinsam transaktional ausgeführt.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]},"description":"Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete geben die angefragte Datei bzw. das Objekt und das Ergebnis an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"design_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Buchungsdesign löschen","value":{"id":"design_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"deleteDesign","line":150},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/design Buchungsdesign auflisten Liest die vorhandenen Buchungsdesigns mit Bootstrap-Zuordnung, Name, Farbe und Buchungswarenkorb-Einstellung. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_design - Dokumentation: /docs/api/v2/operations/get-v2-design-3621d6572671/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/design' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_design","summary":"Buchungsdesign auflisten","description":"Liest die vorhandenen Buchungsdesigns mit Bootstrap-Zuordnung, Name, Farbe und Buchungswarenkorb-Einstellung. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"design_012345abcdef_123456":{"id":"design_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","examples":["design_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"bootstrap":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","examples":["design_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"bootstrap":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"getDesign","line":212},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/design Buchungsdesign anlegen Erstellt ein Buchungsdesign durch Kopieren eines bestehenden eigenen oder öffentlichen Vorlage-Designs. Zusätzlich zu name ist data.design erforderlich. Widgets werden kopiert, fehlende Standard-Formularfelder ergänzt und das Buchungswarenkorb-Verhalten sowie Bootstrap bei fehlender expliziter Angabe übernommen. data akzeptiert einen Datensatz oder ein Array mit bis zu 100 Datensätzen; bei einem Batch enthält data.id ein Array öffentlicher IDs. - Operation-ID: post_v2_design - Dokumentation: /docs/api/v2/operations/post-v2-design-40bba1ac0873/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Sommerkurse", "design": "design_012345abcdef_123456", "booking_cart_enabled": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/design' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Sommerkurse", "design": "design_012345abcdef_123456", "booking_cart_enabled": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Sommerkurse\",\"design\":\"design_012345abcdef_123456\",\"booking_cart_enabled\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_design","summary":"Buchungsdesign anlegen","description":"Erstellt ein Buchungsdesign durch Kopieren eines bestehenden eigenen oder öffentlichen Vorlage-Designs. Zusätzlich zu name ist data.design erforderlich. Widgets werden kopiert, fehlende Standard-Formularfelder ergänzt und das Buchungswarenkorb-Verhalten sowie Bootstrap bei fehlender expliziter Angabe übernommen. data akzeptiert einen Datensatz oder ein Array mit bis zu 100 Datensätzen; bei einem Batch enthält data.id ein Array öffentlicher IDs.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert, öffentliche id und bei Uploads signierte upload_url; öffentliche Dateien liefern zusätzlich cdn_url. Bei fehlgeschlagenem Transaktionsabschluss enthält data insert=false und id=null; error ist ED80100.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"design_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"oneOf":[{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]}},{"type":"null"}],"description":"Bei erfolgreichem Einzelinsert öffentliche v2-ID, bei erfolgreichem Batch ein Array öffentlicher IDs; bei fehlgeschlagenem Transaktionsabschluss null zusammen mit insert=false und error=ED80100."}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"bootstrap":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":39},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":52},"color":{"description":"Canonical #RGB/#RRGGBB. Also accepts unprefixed 4/7 hex chars; strings of other lengths clear. Length 4/7 nonhex rejects. Not equivalent to strict CSS hex regex. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nfalse or string length other than 4/7 -> empty aP string. At length4/7, hexadecimal after ltrim(all leading #) is required; nonhex returns false. Empty output -> false.\n\nRuntime preconditions / rejection: Length4/7 nonhex text returns strict false -> ED80100.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["#a1b",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":85,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","design"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"bootstrap":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":39},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":52},"color":{"description":"Canonical #RGB/#RRGGBB. Also accepts unprefixed 4/7 hex chars; strings of other lengths clear. Length 4/7 nonhex rejects. Not equivalent to strict CSS hex regex. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nfalse or string length other than 4/7 -> empty aP string. At length4/7, hexadecimal after ltrim(all leading #) is required; nonhex returns false. Empty output -> false.\n\nRuntime preconditions / rejection: Length4/7 nonhex text returns strict false -> ED80100.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["#a1b",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":85,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","design"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Buchungsdesign anlegen","value":{"data":{"name":"Sommerkurse","design":"design_012345abcdef_123456","booking_cart_enabled":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"postDesign","line":117},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/design Buchungsdesign ändern Ändert Name, Bootstrap-Zuordnung, Farbe oder Buchungswarenkorb-Einstellung eines bestehenden Buchungsdesigns. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_design - Dokumentation: /docs/api/v2/operations/put-v2-design-4f42b1fede22/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Sommerdesign" }, "id": "design_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/design' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Sommerdesign" }, "id": "design_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Sommerdesign\"},\"id\":\"design_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_design","summary":"Buchungsdesign ändern","description":"Ändert Name, Bootstrap-Zuordnung, Farbe oder Buchungswarenkorb-Einstellung eines bestehenden Buchungsdesigns. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"design_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"bootstrap":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":39},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":52},"color":{"description":"Canonical #RGB/#RRGGBB. Also accepts unprefixed 4/7 hex chars; strings of other lengths clear. Length 4/7 nonhex rejects. Not equivalent to strict CSS hex regex. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nfalse or string length other than 4/7 -> empty aP string. At length4/7, hexadecimal after ltrim(all leading #) is required; nonhex returns false. Empty output -> false.\n\nRuntime preconditions / rejection: Length4/7 nonhex text returns strict false -> ED80100.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["#a1b",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":85,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Buchungsdesign ändern","value":{"data":{"name":"Sommerdesign"},"id":"design_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"updateDesign","line":134},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/design/{id} Buchungsdesign löschen Löscht ein eigenes Buchungsdesign und dessen Widgets. Die Löschlogik setzt betroffene Designreferenzen zurück und ersetzt ein gelöschtes Standarddesign durch die Systemvorlage. Die Datenbankänderungen werden gemeinsam transaktional ausgeführt. - Operation-ID: delete_v2_design_id - Dokumentation: /docs/api/v2/operations/delete-v2-design-id-dbbbce0abf1e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "design_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/design/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "design_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"design_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_design_id","summary":"Buchungsdesign löschen","description":"Löscht ein eigenes Buchungsdesign und dessen Widgets. Die Löschlogik setzt betroffene Designreferenzen zurück und ersetzt ein gelöschtes Standarddesign durch die Systemvorlage. Die Datenbankänderungen werden gemeinsam transaktional ausgeführt.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete geben die angefragte Datei bzw. das Objekt und das Ergebnis an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"design_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},"additionalProperties":true,"description":"Öffentliche ID des zu löschenden Datensatzes. Bei einer ID im Pfad ist kein JSON-Body erforderlich; zusätzliche Create-/Update-Felder werden beim Löschen nicht verarbeitet."},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"id":"design_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"deleteDesign","line":150},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/design/{id} Buchungsdesign lesen Liest die vorhandenen Buchungsdesigns mit Bootstrap-Zuordnung, Name, Farbe und Buchungswarenkorb-Einstellung. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_design_id - Dokumentation: /docs/api/v2/operations/get-v2-design-id-dd748e1775cc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/design/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_design_id","summary":"Buchungsdesign lesen","description":"Liest die vorhandenen Buchungsdesigns mit Bootstrap-Zuordnung, Name, Farbe und Buchungswarenkorb-Einstellung. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"design_012345abcdef_123456":{"id":"design_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","examples":["design_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"bootstrap":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","examples":["design_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"bootstrap":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"getDesign","line":212},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/design/{id} Buchungsdesign ändern Ändert Name, Bootstrap-Zuordnung, Farbe oder Buchungswarenkorb-Einstellung eines bestehenden Buchungsdesigns. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_design_id - Dokumentation: /docs/api/v2/operations/put-v2-design-id-ea63094c5abc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Sommerdesign" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/design/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Sommerdesign" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Sommerdesign\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_design_id","summary":"Buchungsdesign ändern","description":"Ändert Name, Bootstrap-Zuordnung, Farbe oder Buchungswarenkorb-Einstellung eines bestehenden Buchungsdesigns. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"design_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["design_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"bootstrap":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":39},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":52},"color":{"description":"Canonical #RGB/#RRGGBB. Also accepts unprefixed 4/7 hex chars; strings of other lengths clear. Length 4/7 nonhex rejects. Not equivalent to strict CSS hex regex. Canonical representations are shown; other PHP scalar coercions are not presented as rejection constraints.\n\nfalse or string length other than 4/7 -> empty aP string. At length4/7, hexadecimal after ltrim(all leading #) is required; nonhex returns false. Empty output -> false.\n\nRuntime preconditions / rejection: Length4/7 nonhex text returns strict false -> ED80100.","x-edoobox-canonicalType":"string or false","x-edoobox-schemaStatus":"source-reviewed","examples":["#a1b",false],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":85,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Buchungsdesign ändern","value":{"data":{"name":"Sommerdesign"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"updateDesign","line":134},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/design/list Buchungsdesign auflisten Liest die vorhandenen Buchungsdesigns mit Bootstrap-Zuordnung, Name, Farbe und Buchungswarenkorb-Einstellung. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_design_list - Dokumentation: /docs/api/v2/operations/get-v2-design-list-45f38fa34a7d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/design/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_design_list","summary":"Buchungsdesign auflisten","description":"Liest die vorhandenen Buchungsdesigns mit Bootstrap-Zuordnung, Name, Farbe und Buchungswarenkorb-Einstellung. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"design_012345abcdef_123456":{"id":"design_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","examples":["design_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"bootstrap":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","examples":["design_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"bootstrap":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"getDesign","line":212},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/design/list/LUT Öffentliches Vorlage-Design zum Kopieren lesen Liefert das systemseitige Vorlage-Design mit öffentlicher design-ID, Bootstrap-Zuordnung, Name, Farbe und Buchungswarenkorb-Einstellung. Diese design-ID kann POST /design als data.design verwenden; diese Route listet keine frei wählbaren eigenen Designs. - Operation-ID: get_v2_design_list_LUT - Dokumentation: /docs/api/v2/operations/get-v2-design-list-lut-4d60373a44eb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/design/list/LUT' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design/list/LUT"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design/list/LUT" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_design_list_LUT","summary":"Öffentliches Vorlage-Design zum Kopieren lesen","description":"Liefert das systemseitige Vorlage-Design mit öffentlicher design-ID, Bootstrap-Zuordnung, Name, Farbe und Buchungswarenkorb-Einstellung. Diese design-ID kann POST /design als data.design verwenden; diese Route listet keine frei wählbaren eigenen Designs.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Nach öffentlicher design-ID indiziertes Objekt mit genau dem System-Vorlage-Design.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"design_012345abcdef_123456":{"id":"design_012345abcdef_123456","bootstrap":"bootstrap_012345abcdef_123456","name":"Standarddesign","color":"#336699","booking_cart_enabled":false}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","examples":["design_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"bootstrap":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["bootstrap_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Design.php","line":65,"lineStart":65,"lineEnd":84,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"771000c8566835f13d6d1779f5f4cc536ea201dea6a35b330d16edcbcf6550a5","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Design.php","line":117,"lineStart":117,"lineEnd":139,"sha256":"30fc7f721958cb19e4ae31703698687fff67518d807af84f591f3526d2ef3fd8","spanSha256":"e42660c8c4b9e9274c064605ffdad60d0943bf78a429ecde1334840e2696d01f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"booking_cart_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":85,"type":"boolean"}},"additionalProperties":true,"required":["id","bootstrap","name","color","booking_cart_enabled"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Widgetfunc.php","line":50},"reason":"Lookup/configuration catalog reads; template construction uses existing rows only."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"getDesign","line":212,"branch":{"file":"application/models/v2/Design.php","line":217}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/design/list/templates Verfügbare Seitenvorlagen eines Buchungsdesigns auflisten Liefert die geordnete Auswahl an Seitenvorlagen, etwa Angebotsübersicht, Angebotsdetails, Buchungsformular, Kopf und Fußbereich. content_panel und segment ordnen die Vorlage im Designer ein. Zusätzliche Vorlagen sind von Multi-Offer und dem Membership-Plugin des Kontos abhängig. Die einbuchstabigen IDs sind Vorlagenschlüssel und keine Datenbank-IDs. - Operation-ID: get_v2_design_list_templates - Dokumentation: /docs/api/v2/operations/get-v2-design-list-templates-bae7e4caf5fb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/design/list/templates' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/design/list/templates"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/design/list/templates" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_design_list_templates","summary":"Verfügbare Seitenvorlagen eines Buchungsdesigns auflisten","description":"Liefert die geordnete Auswahl an Seitenvorlagen, etwa Angebotsübersicht, Angebotsdetails, Buchungsformular, Kopf und Fußbereich. content_panel und segment ordnen die Vorlage im Designer ein. Zusätzliche Vorlagen sind von Multi-Offer und dem Membership-Plugin des Kontos abhängig. Die einbuchstabigen IDs sind Vorlagenschlüssel und keine Datenbank-IDs.","tags":["design"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine sortierte Liste der für das Konto angebotenen Seitenvorlagen.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"a","name":"Angebotsübersicht","help_text":"Übersicht der verfügbaren Angebote","icon":"file","order":1,"content_panel":"body","segment":"tender","segment_name":"Ausschreibung"}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"help_text":{"type":"string"},"icon":{"type":"string"},"order":{"type":"integer"},"content_panel":{},"segment":{"type":["string","null"]},"segment_name":{"type":["string","null"]}},"additionalProperties":true,"required":["id","name","help_text","icon","order","content_panel"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Widgetfunc.php","line":50},"reason":"Lookup/configuration catalog reads; template construction uses existing rows only."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Design.php","method":"getDesign","line":212,"branch":{"file":"application/models/v2/Design.php","line":219}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"design","short":"desi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/dinaco Dinaco-Veranstaltung löschen Markiert Dinaco-Veranstaltung als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang. - Operation-ID: delete_v2_dinaco - Dokumentation: /docs/api/v2/operations/delete-v2-dinaco-bf322d4f112d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "dinaco_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/dinaco' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "dinaco_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"dinaco_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_dinaco","summary":"Dinaco-Veranstaltung löschen","description":"Markiert Dinaco-Veranstaltung als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und ob eine Zeile geändert wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinaco_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Dinaco-Veranstaltung löschen","value":{"id":"dinaco_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"deleteDinaco","line":365},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dinaco Dinaco-Veranstaltung auflisten Liest Dinaco-Veranstaltung des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind []. - Operation-ID: get_v2_dinaco - Dokumentation: /docs/api/v2/operations/get-v2-dinaco-49c203285d43/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dinaco' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dinaco","summary":"Dinaco-Veranstaltung auflisten","description":"Liest Dinaco-Veranstaltung des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind [].","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält die ausgewählte Veranstaltungs-, Bereichs- oder Berechtigungssammlung; limit enthält Trefferzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"dinaco_012345abcdef_123456":{"id":"dinaco_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"status":{"type":"boolean"},"event":{"type":"string"},"sync_start":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"sync_end":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"first_sync":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"status":{"type":"boolean"},"event":{"type":"string"},"sync_start":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"sync_end":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"first_sync":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"getDinaco","line":393},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/dinaco Dinaco-Veranstaltung erstellen Erstellt Dinaco-Veranstaltung. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein. event muss innerhalb des Kontos eindeutig sein. Der vorwärts laufende Zeitraum muss mindestens einen ganzen Tag und höchstens 14 Tage umfassen und wird auf Kollisionen mit vorhandenen Veranstaltungen geprüft. first_sync wird auf 0 gesetzt. - Operation-ID: post_v2_dinaco - Dokumentation: /docs/api/v2/operations/post-v2-dinaco-1f5f71f01fd4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "event": "BEISPIEL-2027", "sync_start": "2027-04-01T00:00:00+02:00", "sync_end": "2027-04-03T23:59:59+02:00" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/dinaco' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "event": "BEISPIEL-2027", "sync_start": "2027-04-01T00:00:00+02:00", "sync_end": "2027-04-03T23:59:59+02:00" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"event\":\"BEISPIEL-2027\",\"sync_start\":\"2027-04-01T00:00:00+02:00\",\"sync_end\":\"2027-04-03T23:59:59+02:00\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_dinaco","summary":"Dinaco-Veranstaltung erstellen","description":"Erstellt Dinaco-Veranstaltung. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein. event muss innerhalb des Kontos eindeutig sein. Der vorwärts laufende Zeitraum muss mindestens einen ganzen Tag und höchstens 14 Tage umfassen und wird auf Kollisionen mit vorhandenen Veranstaltungen geprüft. first_sync wird auf 0 gesetzt.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue v2-ID oder bei Stapelerstellung eine Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinaco_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"event":{"description":"Dinaco-Veranstaltungsschlüssel; auf 50 Speicherzeichen normalisiert.","x-edoobox-canonicalType":"string"},"sync_start":{"type":"string","description":"Von strtotime lesbarer Beginn."},"sync_end":{"type":"string","description":"Von strtotime lesbares Ende."},"status":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true,"required":["event","sync_start","sync_end"]},{"type":"array","items":{"type":"object","properties":{"event":{"description":"Dinaco-Veranstaltungsschlüssel; auf 50 Speicherzeichen normalisiert.","x-edoobox-canonicalType":"string"},"sync_start":{"type":"string","description":"Von strtotime lesbarer Beginn."},"sync_end":{"type":"string","description":"Von strtotime lesbares Ende."},"status":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true,"required":["event","sync_start","sync_end"]},"minItems":1}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dinaco-Veranstaltung erstellen","value":{"data":{"event":"BEISPIEL-2027","sync_start":"2027-04-01T00:00:00+02:00","sync_end":"2027-04-03T23:59:59+02:00"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"postDinaco","line":106},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/dinaco Dinaco-Veranstaltung ändern Ändert Dinaco-Veranstaltung. Die öffentliche ID muss als id im JSON-Envelope stehen. Bei Veranstaltungen ist nur status veränderbar; bei Bereichen bleibt dinaco unveränderbar. - Operation-ID: put_v2_dinaco - Dokumentation: /docs/api/v2/operations/put-v2-dinaco-26ca936aadf6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": true }, "id": "dinaco_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/dinaco' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": true }, "id": "dinaco_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":true},\"id\":\"dinaco_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_dinaco","summary":"Dinaco-Veranstaltung ändern","description":"Ändert Dinaco-Veranstaltung. Die öffentliche ID muss als id im JSON-Envelope stehen. Bei Veranstaltungen ist nur status veränderbar; bei Bereichen bleibt dinaco unveränderbar.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und das Änderungsergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinaco_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true},"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Dinaco-Veranstaltung ändern","value":{"data":{"status":true},"id":"dinaco_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"updateDinaco","line":306},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/dinaco/{id} Dinaco-Veranstaltung löschen Markiert Dinaco-Veranstaltung als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang. - Operation-ID: delete_v2_dinaco_id - Dokumentation: /docs/api/v2/operations/delete-v2-dinaco-id-e22490ddb6c9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Dinaco-Veranstaltung-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Dinaco-Veranstaltung-ID.","schema":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "dinaco_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "dinaco_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"dinaco_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_dinaco_id","summary":"Dinaco-Veranstaltung löschen","description":"Markiert Dinaco-Veranstaltung als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Dinaco-Veranstaltung-ID.","schema":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und ob eine Zeile geändert wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinaco_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Dinaco-Veranstaltung löschen","value":{"id":"dinaco_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"deleteDinaco","line":365},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dinaco/{id} Dinaco-Veranstaltung lesen Liest Dinaco-Veranstaltung des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind []. - Operation-ID: get_v2_dinaco_id - Dokumentation: /docs/api/v2/operations/get-v2-dinaco-id-9b6df1be0791/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Dinaco-Veranstaltung-ID. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Dinaco-Veranstaltung-ID.","schema":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dinaco_id","summary":"Dinaco-Veranstaltung lesen","description":"Liest Dinaco-Veranstaltung des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind [].","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Dinaco-Veranstaltung-ID.","schema":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält die ausgewählte Veranstaltungs-, Bereichs- oder Berechtigungssammlung; limit enthält Trefferzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"dinaco_012345abcdef_123456":{"id":"dinaco_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"status":{"type":"boolean"},"event":{"type":"string"},"sync_start":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"sync_end":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"first_sync":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"status":{"type":"boolean"},"event":{"type":"string"},"sync_start":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"sync_end":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"first_sync":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"getDinaco","line":393},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/dinaco/{id} Dinaco-Veranstaltung ändern Ändert Dinaco-Veranstaltung. Die öffentliche ID steht im Pfad; id im Envelope hat Vorrang. Bei Veranstaltungen ist nur status veränderbar; bei Bereichen bleibt dinaco unveränderbar. - Operation-ID: put_v2_dinaco_id - Dokumentation: /docs/api/v2/operations/put-v2-dinaco-id-f9a023bede6d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Dinaco-Veranstaltung-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Dinaco-Veranstaltung-ID.","schema":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_dinaco_id","summary":"Dinaco-Veranstaltung ändern","description":"Ändert Dinaco-Veranstaltung. Die öffentliche ID steht im Pfad; id im Envelope hat Vorrang. Bei Veranstaltungen ist nur status veränderbar; bei Bereichen bleibt dinaco unveränderbar.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Dinaco-Veranstaltung-ID.","schema":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und das Änderungsergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinaco_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dinaco-Veranstaltung ändern","value":{"data":{"status":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"updateDinaco","line":306},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/dinaco/{id}/permission Dinaco-Angebotsberechtigung löschen Markiert Dinaco-Angebotsberechtigung als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang. - Operation-ID: delete_v2_dinaco_id_permission - Dokumentation: /docs/api/v2/operations/delete-v2-dinaco-id-permission-1e7e54cc2dd6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche Dinaco-Angebotsberechtigung-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},"description":"Öffentliche Dinaco-Angebotsberechtigung-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "dinacopermission_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}/permission"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "dinacopermission_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}/permission" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"dinacopermission_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_dinaco_id_permission","summary":"Dinaco-Angebotsberechtigung löschen","description":"Markiert Dinaco-Angebotsberechtigung als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},"description":"Öffentliche Dinaco-Angebotsberechtigung-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und ob eine Zeile geändert wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacopermission_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Dinaco-Angebotsberechtigung löschen","value":{"id":"dinacopermission_012345abcdef_123456"}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Dinaco.php","line":374,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"deleteDinaco","line":365,"branch":{"file":"application/models/v2/Dinaco.php","line":376}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/dinaco/{id}/section Dinaco-Zutrittsbereich löschen Markiert Dinaco-Zutrittsbereich als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang. - Operation-ID: delete_v2_dinaco_id_section - Dokumentation: /docs/api/v2/operations/delete-v2-dinaco-id-section-2fa34e9728be/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche Dinaco-Zutrittsbereich-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"description":"Öffentliche Dinaco-Zutrittsbereich-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "dinacosection_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'/section' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}/section"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "dinacosection_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}/section" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"dinacosection_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_dinaco_id_section","summary":"Dinaco-Zutrittsbereich löschen","description":"Markiert Dinaco-Zutrittsbereich als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"description":"Öffentliche Dinaco-Zutrittsbereich-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und ob eine Zeile geändert wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacosection_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Dinaco-Zutrittsbereich löschen","value":{"id":"dinacosection_012345abcdef_123456"}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Dinaco.php","line":371,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"deleteDinaco","line":365,"branch":{"file":"application/models/v2/Dinaco.php","line":373}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/dinaco/{id}/section Dinaco-Zutrittsbereich ändern Ändert Dinaco-Zutrittsbereich. Die öffentliche ID steht im Pfad; id im Envelope hat Vorrang. Bei Veranstaltungen ist nur status veränderbar; bei Bereichen bleibt dinaco unveränderbar. - Operation-ID: put_v2_dinaco_id_section - Dokumentation: /docs/api/v2/operations/put-v2-dinaco-id-section-bdae86a1cb10/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche Dinaco-Zutrittsbereich-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"description":"Öffentliche Dinaco-Zutrittsbereich-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Nebeneingang", "people_count": 1 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'/section' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}/section"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Nebeneingang", "people_count": 1 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}/section" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Nebeneingang\",\"people_count\":1}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_dinaco_id_section","summary":"Dinaco-Zutrittsbereich ändern","description":"Ändert Dinaco-Zutrittsbereich. Die öffentliche ID steht im Pfad; id im Envelope hat Vorrang. Bei Veranstaltungen ist nur status veränderbar; bei Bereichen bleibt dinaco unveränderbar.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"description":"Öffentliche Dinaco-Zutrittsbereich-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und das Änderungsergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacosection_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"description":"Bereichsname, auf 255 Speicherzeichen normalisiert.","x-edoobox-canonicalType":"string"},"add_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"sub_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"merge_station":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"people_count":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"multiple_entrence":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"in_out_mode":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dinaco-Zutrittsbereich ändern","value":{"data":{"name":"Nebeneingang","people_count":1}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Dinaco.php","line":311,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"updateDinaco","line":306,"branch":{"file":"application/models/v2/Dinaco.php","line":311}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/dinaco/{id}/sync Dinaco: Teilnehmertickets synchronisieren Startet die Synchronisierung von Teilnehmertickets für die per v2-Pfad-ID ausgewählte eigene Veranstaltung. sync und syncTicket sind gleichbedeutend. Der Server bestätigt vor der Hintergrundverarbeitung und setzt eine Zehn-Minuten-Sperre. Verarbeitet werden nicht gelöschte Veranstaltungen innerhalb ihres Synchronisierungszeitraums; pausierte Konten werden ausgeschlossen. data-Felder werden nicht ausgewertet. - Operation-ID: put_v2_dinaco_id_sync - Dokumentation: /docs/api/v2/operations/put-v2-dinaco-id-sync-4c7ff9ee5eca/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'/sync' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}/sync"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}/sync" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_dinaco_id_sync","summary":"Dinaco: Teilnehmertickets synchronisieren","description":"Startet die Synchronisierung von Teilnehmertickets für die per v2-Pfad-ID ausgewählte eigene Veranstaltung. sync und syncTicket sind gleichbedeutend. Der Server bestätigt vor der Hintergrundverarbeitung und setzt eine Zehn-Minuten-Sperre. Verarbeitet werden nicht gelöschte Veranstaltungen innerhalb ihres Synchronisierungszeitraums; pausierte Konten werden ausgeschlossen. data-Felder werden nicht ausgewertet.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bestätigt den Hintergrundstart, nicht den erfolgreichen Abschluss bei Dinaco.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinaco_012345abcdef_123456","update":true,"sync":"runs in the background"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"update":{"const":true},"sync":{"const":"runs in the background"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Dinaco: Teilnehmertickets synchronisieren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"updateDinaco","line":306,"branch":{"file":"application/models/v2/Dinaco.php","line":324}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/dinaco/{id}/syncSection Dinaco: Zutrittsbereiche und Berechtigungen synchronisieren Startet die Synchronisierung von Zutrittsbereiche und Berechtigungen für die per v2-Pfad-ID ausgewählte eigene Veranstaltung. sync und syncTicket sind gleichbedeutend. Der Server bestätigt vor der Hintergrundverarbeitung und setzt eine Zehn-Minuten-Sperre. Verarbeitet werden nicht gelöschte Veranstaltungen innerhalb ihres Synchronisierungszeitraums; pausierte Konten werden ausgeschlossen. data-Felder werden nicht ausgewertet. - Operation-ID: put_v2_dinaco_id_syncSection - Dokumentation: /docs/api/v2/operations/put-v2-dinaco-id-syncsection-85d1a3796a2f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'/syncSection' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}/syncSection"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}/syncSection" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_dinaco_id_syncSection","summary":"Dinaco: Zutrittsbereiche und Berechtigungen synchronisieren","description":"Startet die Synchronisierung von Zutrittsbereiche und Berechtigungen für die per v2-Pfad-ID ausgewählte eigene Veranstaltung. sync und syncTicket sind gleichbedeutend. Der Server bestätigt vor der Hintergrundverarbeitung und setzt eine Zehn-Minuten-Sperre. Verarbeitet werden nicht gelöschte Veranstaltungen innerhalb ihres Synchronisierungszeitraums; pausierte Konten werden ausgeschlossen. data-Felder werden nicht ausgewertet.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bestätigt den Hintergrundstart, nicht den erfolgreichen Abschluss bei Dinaco.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinaco_012345abcdef_123456","update":true,"sync":"runs in the background"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"update":{"const":true},"sync":{"const":"runs in the background"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Dinaco: Zutrittsbereiche und Berechtigungen synchronisieren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"updateDinaco","line":306,"branch":{"file":"application/models/v2/Dinaco.php","line":338}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/dinaco/{id}/syncTicket Dinaco: Teilnehmertickets synchronisieren Startet die Synchronisierung von Teilnehmertickets für die per v2-Pfad-ID ausgewählte eigene Veranstaltung. sync und syncTicket sind gleichbedeutend. Der Server bestätigt vor der Hintergrundverarbeitung und setzt eine Zehn-Minuten-Sperre. Verarbeitet werden nicht gelöschte Veranstaltungen innerhalb ihres Synchronisierungszeitraums; pausierte Konten werden ausgeschlossen. data-Felder werden nicht ausgewertet. - Operation-ID: put_v2_dinaco_id_syncTicket - Dokumentation: /docs/api/v2/operations/put-v2-dinaco-id-syncticket-9f87e1207c7a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/dinaco/'"${PATH_ID}"'/syncTicket' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/{id}/syncTicket"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/{id}/syncTicket" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_dinaco_id_syncTicket","summary":"Dinaco: Teilnehmertickets synchronisieren","description":"Startet die Synchronisierung von Teilnehmertickets für die per v2-Pfad-ID ausgewählte eigene Veranstaltung. sync und syncTicket sind gleichbedeutend. Der Server bestätigt vor der Hintergrundverarbeitung und setzt eine Zehn-Minuten-Sperre. Verarbeitet werden nicht gelöschte Veranstaltungen innerhalb ihres Synchronisierungszeitraums; pausierte Konten werden ausgeschlossen. data-Felder werden nicht ausgewertet.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["dinaco_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bestätigt den Hintergrundstart, nicht den erfolgreichen Abschluss bei Dinaco.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinaco_012345abcdef_123456","update":true,"sync":"runs in the background"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"update":{"const":true},"sync":{"const":"runs in the background"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Dinaco: Teilnehmertickets synchronisieren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"updateDinaco","line":306,"branch":{"file":"application/models/v2/Dinaco.php","line":325}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/dinaco/event/permission Dinaco-Angebotsberechtigung erstellen Erstellt Dinaco-Angebotsberechtigung. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein. - Operation-ID: post_v2_dinaco_event_permission - Dokumentation: /docs/api/v2/operations/post-v2-dinaco-event-permission-58ce0469c2f1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "dinaco": "dinaco_012345abcdef_123456", "dinacosection": "dinacosection_012345abcdef_123456", "offer": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/dinaco/event/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/event/permission"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "dinaco": "dinaco_012345abcdef_123456", "dinacosection": "dinacosection_012345abcdef_123456", "offer": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/event/permission" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"dinaco\":\"dinaco_012345abcdef_123456\",\"dinacosection\":\"dinacosection_012345abcdef_123456\",\"offer\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_dinaco_event_permission","summary":"Dinaco-Angebotsberechtigung erstellen","description":"Erstellt Dinaco-Angebotsberechtigung. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue v2-ID oder bei Stapelerstellung eine Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacopermission_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"dinacosection":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["dinaco","dinacosection","offer"]},{"type":"array","items":{"type":"object","properties":{"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"dinacosection":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["dinaco","dinacosection","offer"]},"minItems":1}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dinaco-Angebotsberechtigung erstellen","value":{"data":{"dinaco":"dinaco_012345abcdef_123456","dinacosection":"dinacosection_012345abcdef_123456","offer":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Dinaco.php","line":120,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"postDinaco","line":106,"branch":{"file":"application/models/v2/Dinaco.php","line":120}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/dinaco/event/section Dinaco-Zutrittsbereich erstellen Erstellt Dinaco-Zutrittsbereich. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein. - Operation-ID: post_v2_dinaco_event_section - Dokumentation: /docs/api/v2/operations/post-v2-dinaco-event-section-f553a9dbd6f6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "dinaco": "dinaco_012345abcdef_123456", "name": "Haupteingang", "multiple_entrence": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/dinaco/event/section' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/event/section"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "dinaco": "dinaco_012345abcdef_123456", "name": "Haupteingang", "multiple_entrence": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/event/section" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"dinaco\":\"dinaco_012345abcdef_123456\",\"name\":\"Haupteingang\",\"multiple_entrence\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_dinaco_event_section","summary":"Dinaco-Zutrittsbereich erstellen","description":"Erstellt Dinaco-Zutrittsbereich. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue v2-ID oder bei Stapelerstellung eine Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacosection_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"name":{"description":"Bereichsname, auf 255 Speicherzeichen normalisiert.","x-edoobox-canonicalType":"string"},"add_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"sub_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"merge_station":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"people_count":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"multiple_entrence":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"in_out_mode":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true,"required":["dinaco","name"]},{"type":"array","items":{"type":"object","properties":{"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"name":{"description":"Bereichsname, auf 255 Speicherzeichen normalisiert.","x-edoobox-canonicalType":"string"},"add_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"sub_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"merge_station":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"people_count":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"multiple_entrence":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"in_out_mode":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true,"required":["dinaco","name"]},"minItems":1}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dinaco-Zutrittsbereich erstellen","value":{"data":{"dinaco":"dinaco_012345abcdef_123456","name":"Haupteingang","multiple_entrence":true}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Dinaco.php","line":115,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"postDinaco","line":106,"branch":{"file":"application/models/v2/Dinaco.php","line":115}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dinaco/list Dinaco-Veranstaltung auflisten Liest Dinaco-Veranstaltung des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind []. - Operation-ID: get_v2_dinaco_list - Dokumentation: /docs/api/v2/operations/get-v2-dinaco-list-8c713481e327/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dinaco/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dinaco_list","summary":"Dinaco-Veranstaltung auflisten","description":"Liest Dinaco-Veranstaltung des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind [].","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält die ausgewählte Veranstaltungs-, Bereichs- oder Berechtigungssammlung; limit enthält Trefferzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"dinaco_012345abcdef_123456":{"id":"dinaco_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"status":{"type":"boolean"},"event":{"type":"string"},"sync_start":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"sync_end":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"first_sync":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"status":{"type":"boolean"},"event":{"type":"string"},"sync_start":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"sync_end":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."},"first_sync":{"type":["string","boolean"],"description":"ISO-8601 mit Offset, false ohne gültiges Datum."}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"getDinaco","line":393},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/dinaco/list/permission Dinaco-Angebotsberechtigung löschen Markiert Dinaco-Angebotsberechtigung als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang. - Operation-ID: delete_v2_dinaco_list_permission - Dokumentation: /docs/api/v2/operations/delete-v2-dinaco-list-permission-c75317071af3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "dinacopermission_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/dinaco/list/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/list/permission"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "dinacopermission_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/list/permission" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"dinacopermission_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_dinaco_list_permission","summary":"Dinaco-Angebotsberechtigung löschen","description":"Markiert Dinaco-Angebotsberechtigung als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und ob eine Zeile geändert wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacopermission_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Dinaco-Angebotsberechtigung löschen","value":{"id":"dinacopermission_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"deleteDinaco","line":365,"branch":{"file":"application/models/v2/Dinaco.php","line":376}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dinaco/list/permission Dinaco-Angebotsberechtigung auflisten Liest Dinaco-Angebotsberechtigung des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind []. Der letzte Pfadabschnitt section/permission wählt die tatsächliche Struktur. filter.property=dinaco mit einer Veranstaltungs-v2-ID wählt die Veranstaltung; ein fester type-Selektor existiert nicht. - Operation-ID: get_v2_dinaco_list_permission - Dokumentation: /docs/api/v2/operations/get-v2-dinaco-list-permission-53161eb5984e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | Optionale JSON-Filter. dinaco wählt die Veranstaltung; section/permission verwenden eigene ID-Präfixe. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Optionale JSON-Filter. dinaco wählt die Veranstaltung; section/permission verwenden eigene ID-Präfixe.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"dinaco","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dinaco/list/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/list/permission"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/list/permission" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dinaco_list_permission","summary":"Dinaco-Angebotsberechtigung auflisten","description":"Liest Dinaco-Angebotsberechtigung des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind []. Der letzte Pfadabschnitt section/permission wählt die tatsächliche Struktur. filter.property=dinaco mit einer Veranstaltungs-v2-ID wählt die Veranstaltung; ein fester type-Selektor existiert nicht.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Optionale JSON-Filter. dinaco wählt die Veranstaltung; section/permission verwenden eigene ID-Präfixe.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"dinaco","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält die ausgewählte Veranstaltungs-, Bereichs- oder Berechtigungssammlung; limit enthält Trefferzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"dinacopermission_012345abcdef_123456":{"id":"dinacopermission_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"dinacosection":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"dinacosection":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/22","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Dinaco.php","line":236,"lineEnd":308,"sha256":"742e1eb41a3d117b911e2de1fc78cf06f814355d5c0c9237925c9ab99b7d8a33","reason":"Permission Structure selects the association table and defines ID/relation conversions; no hook creates a permission or invokes hardware."},"reason":"Lists existing Dinaco permission associations through the generic read path.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/22","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"getDinaco","line":393,"branch":{"file":"application/models/v2/Dinaco.php","line":404}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/dinaco/list/permission Dinaco-Angebotsberechtigung erstellen Erstellt Dinaco-Angebotsberechtigung. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein. - Operation-ID: post_v2_dinaco_list_permission - Dokumentation: /docs/api/v2/operations/post-v2-dinaco-list-permission-5ac9210cb6d0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "dinaco": "dinaco_012345abcdef_123456", "dinacosection": "dinacosection_012345abcdef_123456", "offer": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/dinaco/list/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/list/permission"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "dinaco": "dinaco_012345abcdef_123456", "dinacosection": "dinacosection_012345abcdef_123456", "offer": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/list/permission" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"dinaco\":\"dinaco_012345abcdef_123456\",\"dinacosection\":\"dinacosection_012345abcdef_123456\",\"offer\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_dinaco_list_permission","summary":"Dinaco-Angebotsberechtigung erstellen","description":"Erstellt Dinaco-Angebotsberechtigung. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue v2-ID oder bei Stapelerstellung eine Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacopermission_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^dinacopermission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacopermission_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"dinacosection":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["dinaco","dinacosection","offer"]},{"type":"array","items":{"type":"object","properties":{"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"dinacosection":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["dinaco","dinacosection","offer"]},"minItems":1}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dinaco-Angebotsberechtigung erstellen","value":{"data":{"dinaco":"dinaco_012345abcdef_123456","dinacosection":"dinacosection_012345abcdef_123456","offer":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"postDinaco","line":106,"branch":{"file":"application/models/v2/Dinaco.php","line":120}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/dinaco/list/section Dinaco-Zutrittsbereich löschen Markiert Dinaco-Zutrittsbereich als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang. - Operation-ID: delete_v2_dinaco_list_section - Dokumentation: /docs/api/v2/operations/delete-v2-dinaco-list-section-e4ab629782ca/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "dinacosection_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/dinaco/list/section' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/list/section"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "dinacosection_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/list/section" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"dinacosection_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_dinaco_list_section","summary":"Dinaco-Zutrittsbereich löschen","description":"Markiert Dinaco-Zutrittsbereich als gelöscht. Die öffentliche ID steht im Pfad oder als id im Query-/JSON-Envelope; JSON-id hat Vorrang.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und ob eine Zeile geändert wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacosection_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Dinaco-Zutrittsbereich löschen","value":{"id":"dinacosection_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"deleteDinaco","line":365,"branch":{"file":"application/models/v2/Dinaco.php","line":373}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dinaco/list/section Dinaco-Zutrittsbereich auflisten Liest Dinaco-Zutrittsbereich des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind []. Der letzte Pfadabschnitt section/permission wählt die tatsächliche Struktur. filter.property=dinaco mit einer Veranstaltungs-v2-ID wählt die Veranstaltung; ein fester type-Selektor existiert nicht. - Operation-ID: get_v2_dinaco_list_section - Dokumentation: /docs/api/v2/operations/get-v2-dinaco-list-section-2a3dd634c89b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | Optionale JSON-Filter. dinaco wählt die Veranstaltung; section/permission verwenden eigene ID-Präfixe. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Optionale JSON-Filter. dinaco wählt die Veranstaltung; section/permission verwenden eigene ID-Präfixe.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"dinaco","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dinaco/list/section' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/list/section"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/list/section" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dinaco_list_section","summary":"Dinaco-Zutrittsbereich auflisten","description":"Liest Dinaco-Zutrittsbereich des gewählten Kontos mit generischen fields/filter/order/limit. data bleibt auch bei Einzelabfragen eine Sammlung; response_type=array liefert eine Liste, sonst eine ID-indizierte Map. Leere Ergebnisse sind []. Der letzte Pfadabschnitt section/permission wählt die tatsächliche Struktur. filter.property=dinaco mit einer Veranstaltungs-v2-ID wählt die Veranstaltung; ein fester type-Selektor existiert nicht.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Optionale JSON-Filter. dinaco wählt die Veranstaltung; section/permission verwenden eigene ID-Präfixe.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"dinaco","expression":"=","value":"dinaco_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data enthält die ausgewählte Veranstaltungs-, Bereichs- oder Berechtigungssammlung; limit enthält Trefferzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"dinacosection_012345abcdef_123456":{"id":"dinacosection_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"name":{"type":"string"},"add_time_in_seconds":{"type":"integer"},"sub_time_in_seconds":{"type":"integer"},"merge_station":{"type":"boolean"},"people_count":{"type":"integer"},"multiple_entrence":{"type":"boolean"},"in_out_mode":{"type":"boolean"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"name":{"type":"string"},"add_time_in_seconds":{"type":"integer"},"sub_time_in_seconds":{"type":"integer"},"merge_station":{"type":"boolean"},"people_count":{"type":"integer"},"multiple_entrence":{"type":"boolean"},"in_out_mode":{"type":"boolean"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/23","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Dinaco.php","line":131,"lineEnd":234,"sha256":"742e1eb41a3d117b911e2de1fc78cf06f814355d5c0c9237925c9ab99b7d8a33","reason":"Section Structure contains scalar/ID conversion closures and an owner_check through the account-owned Dinaco event, without a write hook."},"reason":"Lists existing Dinaco sections through the generic read path.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/23","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"getDinaco","line":393,"branch":{"file":"application/models/v2/Dinaco.php","line":401}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/dinaco/list/section Dinaco-Zutrittsbereich erstellen Erstellt Dinaco-Zutrittsbereich. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein. - Operation-ID: post_v2_dinaco_list_section - Dokumentation: /docs/api/v2/operations/post-v2-dinaco-list-section-a1c7e4036a8a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "dinaco": "dinaco_012345abcdef_123456", "name": "Haupteingang", "multiple_entrence": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/dinaco/list/section' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/list/section"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "dinaco": "dinaco_012345abcdef_123456", "name": "Haupteingang", "multiple_entrence": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/list/section" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"dinaco\":\"dinaco_012345abcdef_123456\",\"name\":\"Haupteingang\",\"multiple_entrence\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_dinaco_list_section","summary":"Dinaco-Zutrittsbereich erstellen","description":"Erstellt Dinaco-Zutrittsbereich. data kann ein Objekt oder eine Liste sein; die Pflichtfelder gelten je Eintrag. Beziehungen müssen öffentliche v2-IDs sein.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue v2-ID oder bei Stapelerstellung eine Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacosection_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"name":{"description":"Bereichsname, auf 255 Speicherzeichen normalisiert.","x-edoobox-canonicalType":"string"},"add_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"sub_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"merge_station":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"people_count":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"multiple_entrence":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"in_out_mode":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true,"required":["dinaco","name"]},{"type":"array","items":{"type":"object","properties":{"dinaco":{"type":"string","pattern":"^dinaco_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinaco_012345abcdef_123456"]},"name":{"description":"Bereichsname, auf 255 Speicherzeichen normalisiert.","x-edoobox-canonicalType":"string"},"add_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"sub_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"merge_station":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"people_count":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"multiple_entrence":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"in_out_mode":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true,"required":["dinaco","name"]},"minItems":1}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dinaco-Zutrittsbereich erstellen","value":{"data":{"dinaco":"dinaco_012345abcdef_123456","name":"Haupteingang","multiple_entrence":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"postDinaco","line":106,"branch":{"file":"application/models/v2/Dinaco.php","line":115}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/dinaco/list/section Dinaco-Zutrittsbereich ändern Ändert Dinaco-Zutrittsbereich. Die öffentliche ID muss als id im JSON-Envelope stehen. Bei Veranstaltungen ist nur status veränderbar; bei Bereichen bleibt dinaco unveränderbar. - Operation-ID: put_v2_dinaco_list_section - Dokumentation: /docs/api/v2/operations/put-v2-dinaco-list-section-692b8094da9c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Nebeneingang", "people_count": 1 }, "id": "dinacosection_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/dinaco/list/section' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dinaco/list/section"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Nebeneingang", "people_count": 1 }, "id": "dinacosection_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dinaco/list/section" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Nebeneingang\",\"people_count\":1},\"id\":\"dinacosection_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_dinaco_list_section","summary":"Dinaco-Zutrittsbereich ändern","description":"Ändert Dinaco-Zutrittsbereich. Die öffentliche ID muss als id im JSON-Envelope stehen. Bei Veranstaltungen ist nur status veränderbar; bei Bereichen bleibt dinaco unveränderbar.","tags":["dinaco"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data nennt die öffentliche ID und das Änderungsergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dinacosection_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"description":"Bereichsname, auf 255 Speicherzeichen normalisiert.","x-edoobox-canonicalType":"string"},"add_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"sub_time_in_seconds":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"merge_station":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"people_count":{"type":["number","string"],"description":"Zahl bzw. numerischer Text; PHP normalisiert zur Ganzzahl."},"multiple_entrence":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"},"in_out_mode":{"description":"PHP-Wahrheitsvergleich normalisiert auf 1/0; Ausgabe boolean.","examples":[true],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true},"id":{"type":"string","pattern":"^dinacosection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["dinacosection_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Dinaco-Zutrittsbereich ändern","value":{"data":{"name":"Nebeneingang","people_count":1},"id":"dinacosection_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dinaco.php","method":"updateDinaco","line":306,"branch":{"file":"application/models/v2/Dinaco.php","line":311}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dinaco","short":"dina","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dunning Mahnkonfiguration anzeigen Liefert Aktivierung und Regeln getrennt für Rechnungen und Buchungen ohne Rechnung. Legt bei Bedarf eine deaktivierte Standardkonfiguration und Standardregeln an. Die Oberfläche erhält außerdem die verfügbaren Kontexte und maximal zehn Regeln je Kontext. - Operation-ID: get_v2_dunning - Dokumentation: /docs/api/v2/operations/get-v2-dunning-bb8505475d4a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dunning' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dunning"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dunning" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dunning","summary":"Mahnkonfiguration anzeigen","description":"Liefert Aktivierung und Regeln getrennt für Rechnungen und Buchungen ohne Rechnung. Legt bei Bedarf eine deaktivierte Standardkonfiguration und Standardregeln an. Die Oberfläche erhält außerdem die verfügbaren Kontexte und maximal zehn Regeln je Kontext.","tags":["dunning"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Konfigurationsobjekt mit enabled, rules, contexts und max_rules_per_context.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"enabled":false,"rules":{"invoice":[],"booking_without_invoice":[]},"max_rules_per_context":10,"contexts":{"invoice":{"id":"invoice","name":""},"booking_without_invoice":{"id":"booking_without_invoice","name":""}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"rules":{"type":"object","properties":{"invoice":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"type":"integer"},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"type":"number"},"level":{"type":"integer"},"delivery":{"type":"string"},"email_template":{"type":"integer"},"post_template":{"type":"integer"},"active":{"type":"boolean"},"status":{"type":"integer"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"booking_without_invoice":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"type":"integer"},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"type":"number"},"level":{"type":"integer"},"delivery":{"type":"string"},"email_template":{"type":"integer"},"post_template":{"type":"integer"},"active":{"type":"boolean"},"status":{"type":"integer"},"sort_order":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true},"max_rules_per_context":{"const":10},"contexts":{"type":"object","properties":{"invoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"booking_without_invoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Cancellationcondition.php","line":190},"reason":"GET ensures default groups/configuration and can INSERT missing rows (Dunning.php206)."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dunning.php","method":"getDunning","line":26},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dunning","short":"dunn","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dunning/{id} Mahnvorgang prüfen Liefert einen Mahnvorgang des aktuellen Kontos mit Status, offenen Beträgen und verknüpften Buchungen. id ist eine öffentliche dunningentry-ID aus list. Legt bei Bedarf Standardkonfiguration und Regeln an. - Operation-ID: get_v2_dunning_id - Dokumentation: /docs/api/v2/operations/get-v2-dunning-id-a4b598dee0a5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Mahnvorgangs-ID aus dunning/list. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Mahnvorgangs-ID aus dunning/list.","schema":{"type":"string","pattern":"^dunningentry_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningentry_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dunning/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dunning/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dunning/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dunning_id","summary":"Mahnvorgang prüfen","description":"Liefert einen Mahnvorgang des aktuellen Kontos mit Status, offenen Beträgen und verknüpften Buchungen. id ist eine öffentliche dunningentry-ID aus list. Legt bei Bedarf Standardkonfiguration und Regeln an.","tags":["dunning"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Mahnvorgangs-ID aus dunning/list.","schema":{"type":"string","pattern":"^dunningentry_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningentry_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ein einzelnes Mahnvorgangsobjekt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dunningentry_012345abcdef_123456","context":"invoice","rule":"dunningrule_012345abcdef_123456","leading_booking":"booking_012345abcdef_123456","invoice":"invoice_012345abcdef_123456","invoice_number":"INV-EXAMPLE","fee":false,"level":1,"action":"reminder","delivery":"email","status":"sent","amount_open":120,"payment_open":true,"fee_amount":0,"currency":"CHF","reference":"INV-EXAMPLE","bookings":[{"id":"booking_012345abcdef_123456","number":"EXAMPLE-BOOKING","name":"Beispielkurs","leading":true}],"created_at":1792051200,"processed_at":1792051200}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningentry_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningentry_012345abcdef_123456"]},"context":{"type":"string"},"rule":{"anyOf":[{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},{"const":false}]},"leading_booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},{"const":false}]},"invoice_number":{"type":"string"},"fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["fee_012345abcdef_123456"]},{"const":false}]},"level":{"type":"integer"},"action":{"type":"string"},"delivery":{"type":"string"},"status":{"type":"string"},"amount_open":{"type":"number"},"payment_open":{"type":"boolean"},"fee_amount":{"type":"number"},"currency":{"type":"string"},"reference":{"type":"string"},"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"number":{"type":"string"},"name":{"type":"string"},"leading":{"type":"boolean"}},"additionalProperties":true}},"created_at":{"type":"integer"},"processed_at":{"type":"integer"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Cancellationcondition.php","line":190},"reason":"GET ensures default groups/configuration and can INSERT missing rows (Dunning.php206)."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dunning.php","method":"getDunning","line":26},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dunning","short":"dunn","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dunning/config Mahnkonfiguration anzeigen Liefert Aktivierung und Regeln getrennt für Rechnungen und Buchungen ohne Rechnung. Legt bei Bedarf eine deaktivierte Standardkonfiguration und Standardregeln an. Die Oberfläche erhält außerdem die verfügbaren Kontexte und maximal zehn Regeln je Kontext. - Operation-ID: get_v2_dunning_config - Dokumentation: /docs/api/v2/operations/get-v2-dunning-config-84da9600b9e3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dunning/config' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dunning/config"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dunning/config" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dunning_config","summary":"Mahnkonfiguration anzeigen","description":"Liefert Aktivierung und Regeln getrennt für Rechnungen und Buchungen ohne Rechnung. Legt bei Bedarf eine deaktivierte Standardkonfiguration und Standardregeln an. Die Oberfläche erhält außerdem die verfügbaren Kontexte und maximal zehn Regeln je Kontext.","tags":["dunning"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Konfigurationsobjekt mit enabled, rules, contexts und max_rules_per_context.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"enabled":false,"rules":{"invoice":[],"booking_without_invoice":[]},"max_rules_per_context":10,"contexts":{"invoice":{"id":"invoice","name":""},"booking_without_invoice":{"id":"booking_without_invoice","name":""}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"rules":{"type":"object","properties":{"invoice":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"type":"integer"},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"type":"number"},"level":{"type":"integer"},"delivery":{"type":"string"},"email_template":{"type":"integer"},"post_template":{"type":"integer"},"active":{"type":"boolean"},"status":{"type":"integer"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"booking_without_invoice":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"type":"integer"},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"type":"number"},"level":{"type":"integer"},"delivery":{"type":"string"},"email_template":{"type":"integer"},"post_template":{"type":"integer"},"active":{"type":"boolean"},"status":{"type":"integer"},"sort_order":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true},"max_rules_per_context":{"const":10},"contexts":{"type":"object","properties":{"invoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"booking_without_invoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Dunning.php","line":26,"lineEnd":64,"sha256":"6dd6d72c9c9905a1e8cdedc11cbbbcd6b466ab32bd826501b291a74e2a2216bf","reason":"GET calls ensureDefaultConfig before returning formatted config."},"reason":"Reads dunning configuration after seeding missing account configuration and built-in context rules.","review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/24","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-01.json","pointer":"/operations/24","sha256":"98bf744c7b83fe3aef56618efbe2f3f1c34a5a4790833fa03324cf7ba696c55d","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dunning.php","method":"getDunning","line":26},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dunning","short":"dunn","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/dunning/config Mahnregeln und Aktivierung speichern Speichert enabled (ohne Angabe false) und ersetzt bei vorhandenem rules beide Regelgruppen vollständig. Regeln werden nach Tagen und sort_order sortiert; höchstens zehn je Kontext und keine Regel nach einer Stornierungsregel. Nicht-Mahngebühren werden auf 0 gesetzt; cancellation erhält keinen Versandweg. Regel-IDs dürfen nur aus dem eigenen Konto stammen; Regeln ohne passende ID werden angelegt. - Operation-ID: put_v2_dunning_config - Dokumentation: /docs/api/v2/operations/put-v2-dunning-config-edbd02da6e4d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "enabled": true, "rules": { "invoice": [ { "days_without_payment": 7, "action": "reminder", "delivery": "email", "level": 1, "name_translations": { "de": "Erste Zahlungserinnerung" }, "active": true } ], "booking_without_invoice": [] } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/dunning/config' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dunning/config"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "enabled": true, "rules": { "invoice": [ { "days_without_payment": 7, "action": "reminder", "delivery": "email", "level": 1, "name_translations": { "de": "Erste Zahlungserinnerung" }, "active": true } ], "booking_without_invoice": [] } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dunning/config" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"enabled\":true,\"rules\":{\"invoice\":[{\"days_without_payment\":7,\"action\":\"reminder\",\"delivery\":\"email\",\"level\":1,\"name_translations\":{\"de\":\"Erste Zahlungserinnerung\"},\"active\":true}],\"booking_without_invoice\":[]}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_dunning_config","summary":"Mahnregeln und Aktivierung speichern","description":"Speichert enabled (ohne Angabe false) und ersetzt bei vorhandenem rules beide Regelgruppen vollständig. Regeln werden nach Tagen und sort_order sortiert; höchstens zehn je Kontext und keine Regel nach einer Stornierungsregel. Nicht-Mahngebühren werden auf 0 gesetzt; cancellation erhält keinen Versandweg. Regel-IDs dürfen nur aus dem eigenen Konto stammen; Regeln ohne passende ID werden angelegt.","tags":["dunning"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update=true und die gespeicherte Konfiguration.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"config":{"enabled":false,"rules":{"invoice":[],"booking_without_invoice":[]},"max_rules_per_context":10,"contexts":{"invoice":{"id":"invoice","name":""},"booking_without_invoice":{"id":"booking_without_invoice","name":""}}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"config":{"type":"object","properties":{"enabled":{"type":"boolean"},"rules":{"type":"object","properties":{"invoice":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"type":"integer"},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"type":"number"},"level":{"type":"integer"},"delivery":{"type":"string"},"email_template":{"type":"integer"},"post_template":{"type":"integer"},"active":{"type":"boolean"},"status":{"type":"integer"},"sort_order":{"type":"integer"}},"additionalProperties":true}},"booking_without_invoice":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"type":"integer"},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"type":"number"},"level":{"type":"integer"},"delivery":{"type":"string"},"email_template":{"type":"integer"},"post_template":{"type":"integer"},"active":{"type":"boolean"},"status":{"type":"integer"},"sort_order":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true},"max_rules_per_context":{"const":10},"contexts":{"type":"object","properties":{"invoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true},"booking_without_invoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"enabled":{"description":"boolValue: true/1 beziehungsweise 1,true,yes,y,on,aktiv,active aktivieren; Auslassung deaktiviert.","x-edoobox-canonicalType":"boolean"},"rules":{"anyOf":[{"type":"object","properties":{"invoice":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"name":{"type":"string"},"name_translations":{"anyOf":[{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}},{"type":"string"}],"description":"Sprachkürzel auf Text; alternativ JSON-Text oder einzelner Name in der Kontosprache."},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"number"},"level":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"delivery":{"type":"string"},"email_template":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"post_template":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"active":{"description":"boolValue akzeptiert true, 1 und die Zeichenfolgen 1/true/yes/y/on/aktiv/active; sonst false. Ohne active/status standardmäßig true.","x-edoobox-canonicalType":"boolean"},"status":{"description":"Alternative zu active; active hat Vorrang.","x-edoobox-canonicalType":"boolean"},"sort_order":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"action_type":{"type":"string","enum":["reminder","dunning","cancellation"]},"delivery_type":{"type":"string"},"dunning_level":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"}},"additionalProperties":true}},"booking_without_invoice":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"name":{"type":"string"},"name_translations":{"anyOf":[{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}},{"type":"string"}],"description":"Sprachkürzel auf Text; alternativ JSON-Text oder einzelner Name in der Kontosprache."},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"number"},"level":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"delivery":{"type":"string"},"email_template":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"post_template":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"active":{"description":"boolValue akzeptiert true, 1 und die Zeichenfolgen 1/true/yes/y/on/aktiv/active; sonst false. Ohne active/status standardmäßig true.","x-edoobox-canonicalType":"boolean"},"status":{"description":"Alternative zu active; active hat Vorrang.","x-edoobox-canonicalType":"boolean"},"sort_order":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"action_type":{"type":"string","enum":["reminder","dunning","cancellation"]},"delivery_type":{"type":"string"},"dunning_level":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"}},"additionalProperties":true}}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"name":{"type":"string"},"name_translations":{"anyOf":[{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}]}},{"type":"string"}],"description":"Sprachkürzel auf Text; alternativ JSON-Text oder einzelner Name in der Kontosprache."},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"number"},"level":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"delivery":{"type":"string"},"email_template":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"post_template":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"active":{"description":"boolValue akzeptiert true, 1 und die Zeichenfolgen 1/true/yes/y/on/aktiv/active; sonst false. Ohne active/status standardmäßig true.","x-edoobox-canonicalType":"boolean"},"status":{"description":"Alternative zu active; active hat Vorrang.","x-edoobox-canonicalType":"boolean"},"sort_order":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"},"action_type":{"type":"string","enum":["reminder","dunning","cancellation"]},"delivery_type":{"type":"string"},"dunning_level":{"description":"Wird serverseitig numerisch normalisiert; Tage, Stufe, Gebühr und Templatewerte werden mindestens 0. Standardwerte gelten bei Auslassung.","x-edoobox-canonicalType":"integer"}},"additionalProperties":true}}],"description":"Vollständiger Ersatz beider Kontexte: fehlende Regeln/Kontexte werden entfernt. Alternativ Liste mit context je Regel (Standard invoice). Höchstens zehn je Kontext, nach cancellation darf keine spätere Regel folgen."}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Mahnregeln und Aktivierung speichern","value":{"data":{"enabled":true,"rules":{"invoice":[{"days_without_payment":7,"action":"reminder","delivery":"email","level":1,"name_translations":{"de":"Erste Zahlungserinnerung"},"active":true}],"booking_without_invoice":[]}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dunning.php","method":"updateDunning","line":90},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dunning","short":"dunn","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/dunning/list Letzte Mahnvorgänge anzeigen Liefert die letzten maximal 100 Mahnvorgänge des Kontos nach absteigender Erstellungs-ID einschließlich Buchungen, Gebühren, Rechnungsbezug und aktuellem payment_open. Die übergebenen Filter, Sortierung und Pagination werden hier nicht ausgewertet. Legt bei Bedarf Standardkonfiguration und Regeln an. - Operation-ID: get_v2_dunning_list - Dokumentation: /docs/api/v2/operations/get-v2-dunning-list-8364828b9791/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/dunning/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dunning/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dunning/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_dunning_list","summary":"Letzte Mahnvorgänge anzeigen","description":"Liefert die letzten maximal 100 Mahnvorgänge des Kontos nach absteigender Erstellungs-ID einschließlich Buchungen, Gebühren, Rechnungsbezug und aktuellem payment_open. Die übergebenen Filter, Sortierung und Pagination werden hier nicht ausgewertet. Legt bei Bedarf Standardkonfiguration und Regeln an.","tags":["dunning"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nach öffentlicher dunningentry-ID indiziertes Objekt, bei leerem Ergebnis [].","content":{"application/json":{"example":{"success":true,"error":false,"data":{"dunningentry_012345abcdef_123456":{"id":"dunningentry_012345abcdef_123456","context":"invoice","rule":"dunningrule_012345abcdef_123456","leading_booking":"booking_012345abcdef_123456","invoice":"invoice_012345abcdef_123456","invoice_number":"INV-EXAMPLE","fee":false,"level":1,"action":"reminder","delivery":"email","status":"sent","amount_open":120,"payment_open":true,"fee_amount":0,"currency":"CHF","reference":"INV-EXAMPLE","bookings":[{"id":"booking_012345abcdef_123456","number":"EXAMPLE-BOOKING","name":"Beispielkurs","leading":true}],"created_at":1792051200,"processed_at":1792051200}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningentry_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningentry_012345abcdef_123456"]},"context":{"type":"string"},"rule":{"anyOf":[{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},{"const":false}]},"leading_booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},{"const":false}]},"invoice_number":{"type":"string"},"fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["fee_012345abcdef_123456"]},{"const":false}]},"level":{"type":"integer"},"action":{"type":"string"},"delivery":{"type":"string"},"status":{"type":"string"},"amount_open":{"type":"number"},"payment_open":{"type":"boolean"},"fee_amount":{"type":"number"},"currency":{"type":"string"},"reference":{"type":"string"},"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"number":{"type":"string"},"name":{"type":"string"},"leading":{"type":"boolean"}},"additionalProperties":true}},"created_at":{"type":"integer"},"processed_at":{"type":"integer"}},"additionalProperties":true}},{"type":"array","maxItems":0}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":250,"reason":"APIPermissions resource mapping to dunn.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"The list is a read of reminder history, but every getDunning call initializes missing disabled configuration and default rules first. These are real persistent writes, not sending reminders.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/0","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/0","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dunning.php","method":"getDunning","line":26},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dunning","short":"dunn","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/dunning/run Fällige Mahnungen ausführen Prüft fällige Rechnungen und Buchungen ohne Rechnung anhand aktiver Regeln. Im normalen Lauf entstehen Mahnvorgänge, gegebenenfalls Mahngebühren, Nachrichten oder Buchungsstornierungen. dry_run=true berechnet Kandidaten ohne diese Folgeaktionen; Standardkonfiguration kann trotzdem angelegt werden. limit wird bei <=0 auf 50 und maximal auf 250 normalisiert. Ein deaktiviertes Mahnwesen verarbeitet nichts. - Operation-ID: post_v2_dunning_run - Dokumentation: /docs/api/v2/operations/post-v2-dunning-run-e5a2170b4afc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "dry_run": true, "limit": 25 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/dunning/run' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/dunning/run"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "dry_run": true, "limit": 25 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/dunning/run" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"dry_run\":true,\"limit\":25}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_dunning_run","summary":"Fällige Mahnungen ausführen","description":"Prüft fällige Rechnungen und Buchungen ohne Rechnung anhand aktiver Regeln. Im normalen Lauf entstehen Mahnvorgänge, gegebenenfalls Mahngebühren, Nachrichten oder Buchungsstornierungen. dry_run=true berechnet Kandidaten ohne diese Folgeaktionen; Standardkonfiguration kann trotzdem angelegt werden. limit wird bei <=0 auf 50 und maximal auf 250 normalisiert. Ein deaktiviertes Mahnwesen verarbeitet nichts.","tags":["dunning"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ausführungszeit, Aktivierung, created/processed und erzeugte oder simulierte entries. created zählt im dry_run die Kandidaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"enabled":true,"run_at":1792051200,"created":0,"processed":0,"entries":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"run_at":{"type":"integer"},"created":{"type":"integer"},"processed":{"type":"integer"},"entries":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningentry_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningentry_012345abcdef_123456"]},"context":{"type":"string"},"rule":{"anyOf":[{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},{"const":false}]},"leading_booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},{"const":false}]},"invoice_number":{"type":"string"},"fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["fee_012345abcdef_123456"]},{"const":false}]},"level":{"type":"integer"},"action":{"type":"string"},"delivery":{"type":"string"},"status":{"type":"string"},"amount_open":{"type":"number"},"payment_open":{"type":"boolean"},"fee_amount":{"type":"number"},"currency":{"type":"string"},"reference":{"type":"string"},"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"number":{"type":"string"},"name":{"type":"string"},"leading":{"type":"boolean"}},"additionalProperties":true}},"created_at":{"type":"integer"},"processed_at":{"type":"integer"}},"additionalProperties":true},{"type":"object","properties":{"dry_run":{"type":"boolean"},"context":{"type":"string"},"rule":{"type":"object","properties":{"id":{"type":"string","pattern":"^dunningrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["dunningrule_012345abcdef_123456"]},"context":{"type":"string","enum":["invoice","booking_without_invoice"]},"days_without_payment":{"type":"integer"},"name":{"type":"string"},"name_translations":{"type":"object","additionalProperties":{"type":"string"}},"action":{"type":"string","enum":["reminder","dunning","cancellation"]},"fee_amount":{"type":"number"},"level":{"type":"integer"},"delivery":{"type":"string"},"email_template":{"type":"integer"},"post_template":{"type":"integer"},"active":{"type":"boolean"},"status":{"type":"integer"},"sort_order":{"type":"integer"}},"additionalProperties":true},"reference":{"type":"string"},"bookings":{"type":"array","items":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]}}},"additionalProperties":true}]}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"dry_run":{"description":"Direkter PHP-Bool-Cast; JSON true/false verwenden. Nichtleere Zeichenfolgen wie \"false\" sind wahr.","x-edoobox-canonicalType":"boolean"},"limit":{"description":"Direkter Integer-Cast; Standard 50, maximal 250, <=0 verwendet 50.","x-edoobox-canonicalType":"integer"}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Fällige Mahnungen ausführen","value":{"data":{"dry_run":true,"limit":25}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Dunning.php","method":"postDunning","line":66},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"dunning","short":"dunn","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription Verfügbare edoobox-Abonnementpläne abrufen Liest sichtbare Pläne und den aktuell verwendeten Plan. Preise richten sich nach Kontowährung; beim aktuellen Plan wird der vertragliche Preis eingesetzt. offer_signup_start wird auch bei expliziter Feldauswahl ergänzt. Ein Pfadselektor oder id-Filter begrenzt die generische Ergebnisliste. - Operation-ID: get_v2_edsubscription - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-728229a317e7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription","summary":"Verfügbare edoobox-Abonnementpläne abrufen","description":"Liest sichtbare Pläne und den aktuell verwendeten Plan. Preise richten sich nach Kontowährung; beim aktuellen Plan wird der vertragliche Preis eingesetzt. offer_signup_start wird auch bei expliziter Feldauswahl ergänzt. Ein Pfadselektor oder id-Filter begrenzt die generische Ergebnisliste.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edsubscription_012345abcdef_123456":{"id":"edsubscription_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/edsubscription Kontopause oder Kontolöschung mit E-Mail-Bestätigung verwalten Die einzige POST-Aktion ist data.action=account_lifecycle für aktive Superadministratoren. send_email_code benötigt einen nichtleeren reason und sendet an die Administrator-E-Mail; verify_email_code bestätigt den sechsstelligen email_code. Das erhaltene confirmation_token erlaubt pause oder delete. pause_until muss zwischen morgen und acht Monaten liegen. delete plant die Löschung nach 30 Tagen; Kontostand und offene Vorgänge können blockieren. Keine Kontoanmeldung und keine Plananlage. - Operation-ID: post_v2_edsubscription - Dokumentation: /docs/api/v2/operations/post-v2-edsubscription-6e75bd8a7dac/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "action": "account_lifecycle", "lifecycle_step": "send_email_code", "reason": "Saisonale Unterbrechung" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/edsubscription' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "action": "account_lifecycle", "lifecycle_step": "send_email_code", "reason": "Saisonale Unterbrechung" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"action\":\"account_lifecycle\",\"lifecycle_step\":\"send_email_code\",\"reason\":\"Saisonale Unterbrechung\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_edsubscription","summary":"Kontopause oder Kontolöschung mit E-Mail-Bestätigung verwalten","description":"Die einzige POST-Aktion ist data.action=account_lifecycle für aktive Superadministratoren. send_email_code benötigt einen nichtleeren reason und sendet an die Administrator-E-Mail; verify_email_code bestätigt den sechsstelligen email_code. Das erhaltene confirmation_token erlaubt pause oder delete. pause_until muss zwischen morgen und acht Monaten liegen. delete plant die Löschung nach 30 Tagen; Kontostand und offene Vorgänge können blockieren. Keine Kontoanmeldung und keine Plananlage.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Schrittabhängige Versandbestätigung, Bestätigungstoken, Pause oder geplante Löschung; 60-Sekunden-Versand-Cooldown und zeitlich begrenzte Codeprüfung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"email_code_sent":true,"resend_after":60}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"email_code_sent":{"type":"boolean"},"resend_after":{"type":"integer"},"email_verified":{"type":"boolean"},"confirmation_token":{"type":"string"},"paused":{"type":"boolean"},"pause_until":{"type":"string"},"delete":{"type":"boolean"},"scheduled_for":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"action":{"const":"account_lifecycle"},"lifecycle_step":{"type":"string"},"reason":{"type":"string","description":"Nach HTML-/Whitespace-Bereinigung nichtleer; Kürzung auf 1000 Zeichen."},"email_code":{"type":"string","description":"Sechs Ziffern nach Entfernen sonstiger Zeichen."},"confirmation_token":{"type":"string","description":"Token aus erfolgreichem verify_email_code."},"pause_until":{"type":"string","format":"date"}},"additionalProperties":true,"required":["action","lifecycle_step"],"oneOf":[{"properties":{"lifecycle_step":{"const":"send_email_code"}},"required":["reason"]},{"properties":{"lifecycle_step":{"const":"verify_email_code"}},"required":["email_code"]},{"properties":{"lifecycle_step":{"const":"pause"}},"required":["confirmation_token","pause_until"]},{"properties":{"lifecycle_step":{"const":"delete"}},"required":["confirmation_token"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kontopause oder Kontolöschung mit E-Mail-Bestätigung verwalten","value":{"data":{"action":"account_lifecycle","lifecycle_step":"send_email_code","reason":"Saisonale Unterbrechung"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"postEdsubscription","line":207},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription/{id} Verfügbare edoobox-Abonnementpläne abrufen Liest sichtbare Pläne und den aktuell verwendeten Plan. Preise richten sich nach Kontowährung; beim aktuellen Plan wird der vertragliche Preis eingesetzt. offer_signup_start wird auch bei expliziter Feldauswahl ergänzt. Ein Pfadselektor oder id-Filter begrenzt die generische Ergebnisliste. - Operation-ID: get_v2_edsubscription_id - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-id-bbc8545b20b7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription_id","summary":"Verfügbare edoobox-Abonnementpläne abrufen","description":"Liest sichtbare Pläne und den aktuell verwendeten Plan. Preise richten sich nach Kontowährung; beim aktuellen Plan wird der vertragliche Preis eingesetzt. offer_signup_start wird auch bei expliziter Feldauswahl ergänzt. Ein Pfadselektor oder id-Filter begrenzt die generische Ergebnisliste.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edsubscription_012345abcdef_123456":{"id":"edsubscription_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/edsubscription/{id}/edcard Gespeicherte Konto-Zahlungskarte entfernen Entfernt die gewählte gespeicherte Karte und sichert die verbleibende Standardkarte. Karten-/Abrechnungsregeln können ablehnen. Der Selektor ist eine edcard-v2-ID, keine Plan-ID. - Operation-ID: delete_v2_edsubscription_id_edcard - Dokumentation: /docs/api/v2/operations/delete-v2-edsubscription-id-edcard-5adc49d409ab/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche edcard-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche edcard-ID.","schema":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/'"${PATH_ID}"'/edcard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/{id}/edcard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/{id}/edcard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_edsubscription_id_edcard","summary":"Gespeicherte Konto-Zahlungskarte entfernen","description":"Entfernt die gewählte gespeicherte Karte und sichert die verbleibende Standardkarte. Karten-/Abrechnungsregeln können ablehnen. Der Selektor ist eine edcard-v2-ID, keine Plan-ID.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche edcard-ID.","schema":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"edcard_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"Gespeicherte Konto-Zahlungskarte entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"deleteEdsubscription","line":303,"branch":{"file":"application/models/v2/Edsubscription.php","line":310}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/{id}/edcard Status und Zahlungsrolle gespeicherter Karte ändern Ändert status, is_default, is_backup oder invalid_reason. Standard- und Ersatzrolle dürfen nicht gleichzeitig gelten; bestimmte Rollenwechsel benötigen eine zweite aktive Karte. Abgelaufene Karten lassen sich nicht aktivieren. Der Selektor ist eine edcard-v2-ID. - Operation-ID: put_v2_edsubscription_id_edcard - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-id-edcard-0d5f1d4dc7f5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche edcard-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche edcard-ID.","schema":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "is_backup": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/'"${PATH_ID}"'/edcard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/{id}/edcard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "is_backup": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/{id}/edcard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"is_backup\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_id_edcard","summary":"Status und Zahlungsrolle gespeicherter Karte ändern","description":"Ändert status, is_default, is_backup oder invalid_reason. Standard- und Ersatzrolle dürfen nicht gleichzeitig gelten; bestimmte Rollenwechsel benötigen eine zweite aktive Karte. Abgelaufene Karten lassen sich nicht aktivieren. Der Selektor ist eine edcard-v2-ID.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche edcard-ID.","schema":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"edcard_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["active","invalid"]},"is_default":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"is_backup":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"invalid_reason":{"type":"string"}},"additionalProperties":true},"id":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Status und Zahlungsrolle gespeicherter Karte ändern","value":{"data":{"is_backup":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":243}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription/{id}/pay/invoice.pdf Konto-Abonnementrechnung als PDF laden Erzeugt die Rechnung zur öffentlichen Referenz aus edinvoice.invoice. Der Pfadselektor ist eine Rechnungsreferenz, keine Plan-ID. Bei fehlender Rechnung kann die Legacy-Ausgabe leer bleiben. - Operation-ID: get_v2_edsubscription_id_pay_invoice_pdf - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-id-pay-invoice-pdf-6387ffe2c767/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Rechnungsreferenz | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Rechnungsreferenz","schema":{"type":"string","description":"Öffentliche Rechnungsreferenz aus edinvoice.invoice."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/'"${PATH_ID}"'/pay/invoice.pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/{id}/pay/invoice.pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/{id}/pay/invoice.pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription_id_pay_invoice_pdf","summary":"Konto-Abonnementrechnung als PDF laden","description":"Erzeugt die Rechnung zur öffentlichen Referenz aus edinvoice.invoice. Der Pfadselektor ist eine Rechnungsreferenz, keine Plan-ID. Bei fehlender Rechnung kann die Legacy-Ausgabe leer bleiben.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Rechnungsreferenz","schema":{"type":"string","description":"Öffentliche Rechnungsreferenz aus edinvoice.invoice."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"PDF-Download; Legacy-Fehler können JSON oder eine leere Antwort liefern.","content":{"application/pdf":{"schema":{"type":"string","contentMediaType":"application/pdf"}},"application/json":{"schema":{"type":"object","properties":{"error":{"type":["string","boolean"]}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/4","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":370,"reason":"APIPermissions resource mapping to edsu.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Renders/downloads an already existing edoobox account invoice, scoped by current account and invoice reference. It does not issue an invoice or record a payment.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/4","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326,"branch":{"file":"application/models/v2/Edsubscriptionfunc.php","line":1210}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription/{id}/pay/receipt.pdf Konto-Zahlungsquittung als PDF laden Erzeugt eine PDF-Quittung zu einer geeigneten gebuchten Kontozahlung. Der Pfadselektor ist eine edtransaction-v2-ID, keine Plan-ID. Ungeeignete Zahlarten können eine leere Antwort liefern. pdf=inline öffnet inline, Standard ist download. - Operation-ID: get_v2_edsubscription_id_pay_receipt_pdf - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-id-pay-receipt-pdf-04dc448718fa/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Konto-Transaktions-ID. | | pdf | query | nein | PDF-Disposition. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Konto-Transaktions-ID.","schema":{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","examples":["edtransaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"pdf","in":"query","required":false,"description":"PDF-Disposition.","schema":{"type":"string","enum":["download","inline"],"default":"download"}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/'"${PATH_ID}"'/pay/receipt.pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/{id}/pay/receipt.pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/{id}/pay/receipt.pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription_id_pay_receipt_pdf","summary":"Konto-Zahlungsquittung als PDF laden","description":"Erzeugt eine PDF-Quittung zu einer geeigneten gebuchten Kontozahlung. Der Pfadselektor ist eine edtransaction-v2-ID, keine Plan-ID. Ungeeignete Zahlarten können eine leere Antwort liefern. pdf=inline öffnet inline, Standard ist download.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Konto-Transaktions-ID.","schema":{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","examples":["edtransaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"pdf","in":"query","required":false,"description":"PDF-Disposition.","schema":{"type":"string","enum":["download","inline"],"default":"download"}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"PDF-Download; Legacy-Fehler können JSON oder eine leere Antwort liefern.","content":{"application/pdf":{"schema":{"type":"string","contentMediaType":"application/pdf"}},"application/json":{"schema":{"type":"object","properties":{"error":{"type":["string","boolean"]}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326,"branch":{"file":"application/models/v2/Edsubscriptionfunc.php","line":1216}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/{id}/select edoobox-Abonnementplan wechseln Wählt den Plan, wenn aktuelle Nutzung und Funktionen in seine Grenzen passen. Ein vorgeschriebener Widerrufsverzicht muss akzeptiert sein. Rechnet bisherige Laufzeit ab, speichert den Plan und beendet eine Kontopause. ED40230 nennt unvereinbare Fähigkeiten. - Operation-ID: put_v2_edsubscription_id_select - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-id-select-d878ca07e31c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche edsubscription-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche edsubscription-ID.","schema":{"type":"string","pattern":"^edsubscription_[a-f0-9]{12}_-?[0-9]+$","examples":["edsubscription_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "withdrawal_waiver_accepted": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/'"${PATH_ID}"'/select' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/{id}/select"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "withdrawal_waiver_accepted": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/{id}/select" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"withdrawal_waiver_accepted\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_id_select","summary":"edoobox-Abonnementplan wechseln","description":"Wählt den Plan, wenn aktuelle Nutzung und Funktionen in seine Grenzen passen. Ein vorgeschriebener Widerrufsverzicht muss akzeptiert sein. Rechnet bisherige Laufzeit ab, speichert den Plan und beendet eine Kontopause. ED40230 nennt unvereinbare Fähigkeiten.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche edsubscription-ID.","schema":{"type":"string","pattern":"^edsubscription_[a-f0-9]{12}_-?[0-9]+$","examples":["edsubscription_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"edsubscription_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^edsubscription_[a-f0-9]{12}_-?[0-9]+$","examples":["edsubscription_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transactiontype":{"type":"string"},"lastedsubscription_id":{"type":"string","pattern":"^edsubscription_[a-f0-9]{12}_-?[0-9]+$","examples":["edsubscription_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"currency":{"type":"string"},"edsubscription":{"type":"object","properties":{},"additionalProperties":true},"inuse":{"type":"object","properties":{"offer_maximum":{"type":"integer"},"user_maximum":{"type":"integer"},"pricecategorie_maximum":{"type":"integer"},"oauth2_provider":{"type":"integer"},"datastructure_function":{"type":"integer"},"edoobox_connect":{"type":"integer"},"offerlist_maximum":{"type":"integer"},"lesson_booking":{"type":"boolean"},"certificate_validation":{"type":"boolean"},"offer_signup_start":{"type":"boolean"},"internal_function":{}},"additionalProperties":true},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"withdrawal_waiver_accepted":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"withdrawal_waiver":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"withdrawalWaiver":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"WithdrawalWaiver":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true},"id":{"type":"string","pattern":"^edsubscription_[a-f0-9]{12}_-?[0-9]+$","examples":["edsubscription_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"edoobox-Abonnementplan wechseln","value":{"data":{"withdrawal_waiver_accepted":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":261}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/account/edcard_setup Zahlungskarte über Stripe SetupIntent speichern step=SetupIntent erzeugt einen Intent mit client_secret zur clientseitigen Bestätigung. step=Save und setup_intent prüfen den bestätigten Intent und speichern die Karte. Rohe Karteninformationen werden hier nicht erwartet. - Operation-ID: put_v2_edsubscription_account_edcard_setup - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-account-edcard-setup-eef282c05dc0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "step": "SetupIntent" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/account/edcard_setup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/account/edcard_setup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "step": "SetupIntent" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/account/edcard_setup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"step\":\"SetupIntent\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_account_edcard_setup","summary":"Zahlungskarte über Stripe SetupIntent speichern","description":"step=SetupIntent erzeugt einen Intent mit client_secret zur clientseitigen Bestätigung. step=Save und setup_intent prüfen den bestätigten Intent und speichern die Karte. Rohe Karteninformationen werden hier nicht erwartet.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"SetupIntent liefert client_secret und setup_intent_id, Save liefert id und saved=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"client_secret":"seti_synthetic_secret_example","setup_intent_id":"seti_synthetic_example"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"client_secret":{"type":"string"},"setup_intent_id":{"type":"string"},"id":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"saved":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"step":{"type":"string","enum":["SetupIntent","Save"]},"setup_intent":{"type":"string"}},"additionalProperties":true,"required":["step"],"if":{"properties":{"step":{"const":"Save"}}},"then":{"required":["setup_intent"]}},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zahlungskarte über Stripe SetupIntent speichern","value":{"data":{"step":"SetupIntent"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/account/mwstnumber_verification Umsatzsteuer-ID mit VIES prüfen Normalisiert die Umsatzsteuer-ID und prüft sie über VIES. Höchstens fünf gültig formatierte Versuche in fünf Minuten pro Konto; der Aufruf verändert den Cache-Prüfzähler. Negative oder fehlgeschlagene Prüfung liefert vat_valid=false. - Operation-ID: put_v2_edsubscription_account_mwstnumber_verification - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-account-mwstnumber-verification-29f0fef67dda/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "vat_number": "DE123456789" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/account/mwstnumber_verification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/account/mwstnumber_verification"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "vat_number": "DE123456789" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/account/mwstnumber_verification" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"vat_number\":\"DE123456789\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_account_mwstnumber_verification","summary":"Umsatzsteuer-ID mit VIES prüfen","description":"Normalisiert die Umsatzsteuer-ID und prüft sie über VIES. Höchstens fünf gültig formatierte Versuche in fünf Minuten pro Konto; der Aufruf verändert den Cache-Prüfzähler. Negative oder fehlgeschlagene Prüfung liefert vat_valid=false.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"vat_valid":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"vat_valid":{"type":"boolean"}},"additionalProperties":true,"required":["vat_valid"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"vat_number":{"type":"string"}},"additionalProperties":true,"required":["vat_number"]},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Umsatzsteuer-ID mit VIES prüfen","value":{"data":{"vat_number":"DE123456789"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/account/pay Guthaben für die edoobox-Kontoabrechnung bezahlen Erstellt eine Zahlung oder Rechnungsreferenz für das Konto. type ist invoice, creditcard oder sofortcom; amount muss mindestens 2 betragen und description nach Bereinigung mindestens zwei Zeichen haben. EUR-Konten benötigen vorher eine Geschäftsbeziehung. Bei creditcard sind step=PaymentIntent, Pay oder PayCard erforderlich: Pay verwendet eine Stripe-PaymentIntent-ID, PayCard eine gespeicherte edcard-v2-ID in paymentid. Zahlungsaufrufe können Geldbewegungen auslösen; fehlgeschlagene oder unklare Antworten nicht blind wiederholen. - Operation-ID: put_v2_edsubscription_account_pay - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-account-pay-cd9c3b1daffb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "type": "invoice", "amount": 100, "description": "Kontoguthaben" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/account/pay' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/account/pay"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "type": "invoice", "amount": 100, "description": "Kontoguthaben" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/account/pay" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"type\":\"invoice\",\"amount\":100,\"description\":\"Kontoguthaben\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_account_pay","summary":"Guthaben für die edoobox-Kontoabrechnung bezahlen","description":"Erstellt eine Zahlung oder Rechnungsreferenz für das Konto. type ist invoice, creditcard oder sofortcom; amount muss mindestens 2 betragen und description nach Bereinigung mindestens zwei Zeichen haben. EUR-Konten benötigen vorher eine Geschäftsbeziehung. Bei creditcard sind step=PaymentIntent, Pay oder PayCard erforderlich: Pay verwendet eine Stripe-PaymentIntent-ID, PayCard eine gespeicherte edcard-v2-ID in paymentid. Zahlungsaufrufe können Geldbewegungen auslösen; fehlgeschlagene oder unklare Antworten nicht blind wiederholen.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Zahlungsart-/schrittabhängig: invoice liefert status und token mit Rechnungsreferenzdaten, sofortcom eine redirect_url, PaymentIntent client_secret und return_url, Kreditkartenzahlung status und requires_action. error=false bestätigt nicht zwingend eine Zahlung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"status":"success","type":"creditcard","requires_action":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"string"},"token":{},"type":{"type":["string","boolean"]},"id":{"type":"string"},"client_secret":{"type":"string"},"error_code":{"type":"string"},"requires_action":{"type":"boolean"},"return_url":{"type":"string"},"redirect_url":{"type":"string"},"error":{"type":["string","boolean"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["invoice","creditcard","sofortcom"]},"amount":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette >= 2."},"description":{"type":"string","description":"Wird bereinigt und auf 35 Zeichen gekürzt; Ergebnis mindestens zwei Zeichen."},"step":{"type":"string","enum":["PaymentIntent","Pay","PayCard"]},"paymentid":{"type":"string","description":"Stripe-PaymentIntent-ID bei Pay oder öffentliche edcard-v2-ID bei PayCard."}},"additionalProperties":true,"required":["type","amount","description"],"if":{"properties":{"type":{"const":"creditcard"}}},"then":{"required":["step"]}},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Guthaben für die edoobox-Kontoabrechnung bezahlen","value":{"data":{"type":"invoice","amount":100,"description":"Kontoguthaben"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/account/promotion_code Verlängerungscode der Konto-Testphase einlösen Verlängert die Testphase mit einem unbenutzten Code um fünf Tage. Ungültige/verwendete Codes liefern update=false. Der Legacy-Schalter reset setzt Testcodes und Testdauer zurück und verändert ebenfalls den Kontozustand. - Operation-ID: put_v2_edsubscription_account_promotion_code - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-account-promotion-code-915f1f10e7ea/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "code": "SYNTHETIC-TRIAL" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/account/promotion_code' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/account/promotion_code"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "code": "SYNTHETIC-TRIAL" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/account/promotion_code" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"code\":\"SYNTHETIC-TRIAL\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_account_promotion_code","summary":"Verlängerungscode der Konto-Testphase einlösen","description":"Verlängert die Testphase mit einem unbenutzten Code um fünf Tage. Ungültige/verwendete Codes liefern update=false. Der Legacy-Schalter reset setzt Testcodes und Testdauer zurück und verändert ebenfalls den Kontozustand.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edsubscription_testinguntildate":"2026-11-01T23:59:59+01:00","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"edsubscription_testinguntildate":{"type":"string"},"update":{"type":"boolean"},"error_code":{"type":"string"},"reset":{"type":"object","properties":{"codes":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"code":{"type":"string","description":"Mindestens vier Zeichen."},"reset":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"anyOf":[{"required":["code"]},{"required":["reset"]}]},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Verlängerungscode der Konto-Testphase einlösen","value":{"data":{"code":"SYNTHETIC-TRIAL"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Edsubscription.php","line":247,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":247}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/account/vat_relationship Geschäftsbeziehung B2B oder B2C festlegen Legt die Geschäftsbeziehung einmalig fest. B2B erfordert vat_number und eine erfolgreiche VIES-Prüfung, B2C keine Umsatzsteuer-ID. Bestehende Beziehungen sind hier nicht änderbar. B2B kann den gespeicherten Abopreis um enthaltene Steuer reduzieren. - Operation-ID: put_v2_edsubscription_account_vat_relationship - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-account-vat-relationship-744962f274c3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "relationship": "B2C" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/account/vat_relationship' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/account/vat_relationship"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "relationship": "B2C" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/account/vat_relationship" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"relationship\":\"B2C\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_account_vat_relationship","summary":"Geschäftsbeziehung B2B oder B2C festlegen","description":"Legt die Geschäftsbeziehung einmalig fest. B2B erfordert vat_number und eine erfolgreiche VIES-Prüfung, B2C keine Umsatzsteuer-ID. Bestehende Beziehungen sind hier nicht änderbar. B2B kann den gespeicherten Abopreis um enthaltene Steuer reduzieren.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","examples":["account_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"},"vat_valid":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"relationship":{"type":"string","enum":["B2B","B2C"]},"vat_number":{"type":"string"}},"additionalProperties":true,"required":["relationship"],"if":{"properties":{"relationship":{"const":"B2B"}}},"then":{"required":["vat_number"]}},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Geschäftsbeziehung B2B oder B2C festlegen","value":{"data":{"relationship":"B2C"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription/list Verfügbare edoobox-Abonnementpläne abrufen Liest sichtbare Pläne und den aktuell verwendeten Plan. Preise richten sich nach Kontowährung; beim aktuellen Plan wird der vertragliche Preis eingesetzt. offer_signup_start wird auch bei expliziter Feldauswahl ergänzt. Ein Pfadselektor oder id-Filter begrenzt die generische Ergebnisliste. - Operation-ID: get_v2_edsubscription_list - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-list-3bfb004acd30/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription_list","summary":"Verfügbare edoobox-Abonnementpläne abrufen","description":"Liest sichtbare Pläne und den aktuell verwendeten Plan. Preise richten sich nach Kontowährung; beim aktuellen Plan wird der vertragliche Preis eingesetzt. offer_signup_start wird auch bei expliziter Feldauswahl ergänzt. Ein Pfadselektor oder id-Filter begrenzt die generische Ergebnisliste.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edsubscription_012345abcdef_123456":{"id":"edsubscription_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edsubscription_012345abcdef_123456"]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":29,"type":"integer"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"string"},"price":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47},"pricecategorie_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"integer"},"offer_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"integer"},"user_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":74,"type":"integer"},"internal_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":83,"type":"boolean"},"datastructure_function":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":92,"type":"boolean"},"edoobox_connect":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"boolean"},"oauth2_provider":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"boolean"},"email_template":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"boolean"},"lesson_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"boolean"},"certificate_validation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"type":"boolean"},"offer_display_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"boolean"},"offer_signup_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":155,"type":"boolean"},"offerlist_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"support":{"enum":["none","basic","premium"]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription/list/edcard Gespeicherte Konto-Zahlungskarten abrufen Liest maskierte Kartendaten mit Ablauf, Aktivstatus und Standard-/Ersatzrolle. Vor der Ausgabe werden abgelaufene Karten invalidiert und die Standardkarte repariert. limit paginiert das Array; Filter und Feldauswahl werden ignoriert. - Operation-ID: get_v2_edsubscription_list_edcard - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-list-edcard-d326f88ccdb9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/edcard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/edcard"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/edcard" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription_list_edcard","summary":"Gespeicherte Konto-Zahlungskarten abrufen","description":"Liest maskierte Kartendaten mit Ablauf, Aktivstatus und Standard-/Ersatzrolle. Vor der Ausgabe werden abgelaufene Karten invalidiert und die Standardkarte repariert. limit paginiert das Array; Filter und Feldauswahl werden ignoriert.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Array maskierter Kartendaten mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":[]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"date_create":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"card_brand":{"type":"string"},"card_last4":{"type":"string"},"card_exp_month":{"type":"integer"},"card_exp_year":{"type":"integer"},"card_holder":{"type":"string"},"status":{"type":"string"},"is_default":{"type":"boolean"},"is_backup":{"type":"boolean"},"invalid_reason":{"type":["string","boolean"]},"can_use_for_charge":{"type":"boolean"}},"additionalProperties":true}},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":370,"reason":"APIPermissions resource mapping to edsu.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Stored card listing reads local card records but first expires saved cards and repairs persisted default/backup flags. No Stripe payment or setup intent is initiated by this GET.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/1","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/1","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326,"branch":{"file":"application/models/v2/Edsubscription.php","line":356}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/list/edcard_setup Zahlungskarte über Stripe SetupIntent speichern step=SetupIntent erzeugt einen Intent mit client_secret zur clientseitigen Bestätigung. step=Save und setup_intent prüfen den bestätigten Intent und speichern die Karte. Rohe Karteninformationen werden hier nicht erwartet. - Operation-ID: put_v2_edsubscription_list_edcard_setup - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-list-edcard-setup-a8a3366f31e0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "step": "SetupIntent" }, "id": "account" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/edcard_setup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/edcard_setup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "step": "SetupIntent" }, "id": "account" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/edcard_setup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"step\":\"SetupIntent\"},\"id\":\"account\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_list_edcard_setup","summary":"Zahlungskarte über Stripe SetupIntent speichern","description":"step=SetupIntent erzeugt einen Intent mit client_secret zur clientseitigen Bestätigung. step=Save und setup_intent prüfen den bestätigten Intent und speichern die Karte. Rohe Karteninformationen werden hier nicht erwartet.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"SetupIntent liefert client_secret und setup_intent_id, Save liefert id und saved=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"client_secret":"seti_synthetic_secret_example","setup_intent_id":"seti_synthetic_example"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"client_secret":{"type":"string"},"setup_intent_id":{"type":"string"},"id":{"type":"string","pattern":"^edcard_[a-f0-9]{12}_-?[0-9]+$","examples":["edcard_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"saved":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"step":{"type":"string","enum":["SetupIntent","Save"]},"setup_intent":{"type":"string"}},"additionalProperties":true,"required":["step"],"if":{"properties":{"step":{"const":"Save"}}},"then":{"required":["setup_intent"]}},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Zahlungskarte über Stripe SetupIntent speichern","value":{"data":{"step":"SetupIntent"},"id":"account"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":239}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription/list/edinvoice Konto-Abonnementrechnungen abrufen Liest Rechnungsreferenzen mit Betrag, Währung, Daten und Zahlungskopplung. invoice ist die öffentliche Referenz für /{reference}/pay/invoice.pdf; id ist eine edinvoice-v2-ID. paid, edtransaction und payment_id beziehen sich auf die tatsächlich verknüpfte Zahlung. - Operation-ID: get_v2_edsubscription_list_edinvoice - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-list-edinvoice-346905c4ec32/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/edinvoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/edinvoice"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/edinvoice" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription_list_edinvoice","summary":"Konto-Abonnementrechnungen abrufen","description":"Liest Rechnungsreferenzen mit Betrag, Währung, Daten und Zahlungskopplung. invoice ist die öffentliche Referenz für /{reference}/pay/invoice.pdf; id ist eine edinvoice-v2-ID. paid, edtransaction und payment_id beziehen sich auf die tatsächlich verknüpfte Zahlung.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edinvoice_012345abcdef_123456":{"id":"edinvoice_012345abcdef_123456","date_create":"2026-10-22T10:00:00+02:00","date_pay":false,"invoice":"SYNTHETIC-INV-2026","description":"Kontoguthaben","amount":100,"currency":"CHF","paid":false,"edtransaction":false,"payment_id":false}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^edinvoice_[a-f0-9]{12}_-?[0-9]+$","examples":["edinvoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"date_create":{"type":["string","boolean"],"description":"ISO-Datumswert aus dQ() oder false bei fehlendem Datum."},"date_pay":{"type":["string","boolean"],"description":"ISO-Datumswert aus dQ() oder false bei fehlendem Datum."},"invoice":{"type":"string"},"description":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"paid":{"type":"boolean"},"edtransaction":{"anyOf":[{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","examples":["edtransaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment_id":{"type":["string","boolean"]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^edinvoice_[a-f0-9]{12}_-?[0-9]+$","examples":["edinvoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"date_create":{"type":["string","boolean"],"description":"ISO-Datumswert aus dQ() oder false bei fehlendem Datum."},"date_pay":{"type":["string","boolean"],"description":"ISO-Datumswert aus dQ() oder false bei fehlendem Datum."},"invoice":{"type":"string"},"description":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"paid":{"type":"boolean"},"edtransaction":{"anyOf":[{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","examples":["edtransaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment_id":{"type":["string","boolean"]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326,"branch":{"file":"application/models/v2/Edsubscription.php","line":360}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/list/mwstnumber_verification Umsatzsteuer-ID mit VIES prüfen Normalisiert die Umsatzsteuer-ID und prüft sie über VIES. Höchstens fünf gültig formatierte Versuche in fünf Minuten pro Konto; der Aufruf verändert den Cache-Prüfzähler. Negative oder fehlgeschlagene Prüfung liefert vat_valid=false. - Operation-ID: put_v2_edsubscription_list_mwstnumber_verification - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-list-mwstnumber-verification-e65515eaa2d1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "vat_number": "DE123456789" }, "id": "account" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/mwstnumber_verification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/mwstnumber_verification"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "vat_number": "DE123456789" }, "id": "account" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/mwstnumber_verification" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"vat_number\":\"DE123456789\"},\"id\":\"account\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_list_mwstnumber_verification","summary":"Umsatzsteuer-ID mit VIES prüfen","description":"Normalisiert die Umsatzsteuer-ID und prüft sie über VIES. Höchstens fünf gültig formatierte Versuche in fünf Minuten pro Konto; der Aufruf verändert den Cache-Prüfzähler. Negative oder fehlgeschlagene Prüfung liefert vat_valid=false.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"vat_valid":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"vat_valid":{"type":"boolean"}},"additionalProperties":true,"required":["vat_valid"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"vat_number":{"type":"string"}},"additionalProperties":true,"required":["vat_number"]},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Umsatzsteuer-ID mit VIES prüfen","value":{"data":{"vat_number":"DE123456789"},"id":"account"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":255}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/list/pay Guthaben für die edoobox-Kontoabrechnung bezahlen Erstellt eine Zahlung oder Rechnungsreferenz für das Konto. type ist invoice, creditcard oder sofortcom; amount muss mindestens 2 betragen und description nach Bereinigung mindestens zwei Zeichen haben. EUR-Konten benötigen vorher eine Geschäftsbeziehung. Bei creditcard sind step=PaymentIntent, Pay oder PayCard erforderlich: Pay verwendet eine Stripe-PaymentIntent-ID, PayCard eine gespeicherte edcard-v2-ID in paymentid. Zahlungsaufrufe können Geldbewegungen auslösen; fehlgeschlagene oder unklare Antworten nicht blind wiederholen. - Operation-ID: put_v2_edsubscription_list_pay - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-list-pay-df72ac35bcbd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "type": "invoice", "amount": 100, "description": "Kontoguthaben" }, "id": "account" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/pay' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/pay"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "type": "invoice", "amount": 100, "description": "Kontoguthaben" }, "id": "account" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/pay" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"type\":\"invoice\",\"amount\":100,\"description\":\"Kontoguthaben\"},\"id\":\"account\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_list_pay","summary":"Guthaben für die edoobox-Kontoabrechnung bezahlen","description":"Erstellt eine Zahlung oder Rechnungsreferenz für das Konto. type ist invoice, creditcard oder sofortcom; amount muss mindestens 2 betragen und description nach Bereinigung mindestens zwei Zeichen haben. EUR-Konten benötigen vorher eine Geschäftsbeziehung. Bei creditcard sind step=PaymentIntent, Pay oder PayCard erforderlich: Pay verwendet eine Stripe-PaymentIntent-ID, PayCard eine gespeicherte edcard-v2-ID in paymentid. Zahlungsaufrufe können Geldbewegungen auslösen; fehlgeschlagene oder unklare Antworten nicht blind wiederholen.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Zahlungsart-/schrittabhängig: invoice liefert status und token mit Rechnungsreferenzdaten, sofortcom eine redirect_url, PaymentIntent client_secret und return_url, Kreditkartenzahlung status und requires_action. error=false bestätigt nicht zwingend eine Zahlung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"status":"success","type":"creditcard","requires_action":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"string"},"token":{},"type":{"type":["string","boolean"]},"id":{"type":"string"},"client_secret":{"type":"string"},"error_code":{"type":"string"},"requires_action":{"type":"boolean"},"return_url":{"type":"string"},"redirect_url":{"type":"string"},"error":{"type":["string","boolean"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string","enum":["invoice","creditcard","sofortcom"]},"amount":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette >= 2."},"description":{"type":"string","description":"Wird bereinigt und auf 35 Zeichen gekürzt; Ergebnis mindestens zwei Zeichen."},"step":{"type":"string","enum":["PaymentIntent","Pay","PayCard"]},"paymentid":{"type":"string","description":"Stripe-PaymentIntent-ID bei Pay oder öffentliche edcard-v2-ID bei PayCard."}},"additionalProperties":true,"required":["type","amount","description"],"if":{"properties":{"type":{"const":"creditcard"}}},"then":{"required":["step"]}},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Guthaben für die edoobox-Kontoabrechnung bezahlen","value":{"data":{"type":"invoice","amount":100,"description":"Kontoguthaben"},"id":"account"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":235}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/list/promotion_code Verlängerungscode der Konto-Testphase einlösen Verlängert die Testphase mit einem unbenutzten Code um fünf Tage. Ungültige/verwendete Codes liefern update=false. Der Legacy-Schalter reset setzt Testcodes und Testdauer zurück und verändert ebenfalls den Kontozustand. - Operation-ID: put_v2_edsubscription_list_promotion_code - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-list-promotion-code-90862d6be570/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "code": "SYNTHETIC-TRIAL" }, "id": "account" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/promotion_code' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/promotion_code"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "code": "SYNTHETIC-TRIAL" }, "id": "account" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/promotion_code" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"code\":\"SYNTHETIC-TRIAL\"},\"id\":\"account\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_list_promotion_code","summary":"Verlängerungscode der Konto-Testphase einlösen","description":"Verlängert die Testphase mit einem unbenutzten Code um fünf Tage. Ungültige/verwendete Codes liefern update=false. Der Legacy-Schalter reset setzt Testcodes und Testdauer zurück und verändert ebenfalls den Kontozustand.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edsubscription_testinguntildate":"2026-11-01T23:59:59+01:00","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"edsubscription_testinguntildate":{"type":"string"},"update":{"type":"boolean"},"error_code":{"type":"string"},"reset":{"type":"object","properties":{"codes":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"code":{"type":"string","description":"Mindestens vier Zeichen."},"reset":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"anyOf":[{"required":["code"]},{"required":["reset"]}]},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Verlängerungscode der Konto-Testphase einlösen","value":{"data":{"code":"SYNTHETIC-TRIAL"},"id":"account"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":247}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription/list/skip_counter Protokoll übersprungener Zahlungssperren lesen Liest Zeitpunkte, Administratoren, edid und Token früherer Überspring-Aktionen. Dieser GET reduziert den Zähler nicht; dafür PUT /list/skip_counter verwenden. - Operation-ID: get_v2_edsubscription_list_skip_counter - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-list-skip-counter-4e653230f9da/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/skip_counter' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/skip_counter"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/skip_counter" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription_list_skip_counter","summary":"Protokoll übersprungener Zahlungssperren lesen","description":"Liest Zeitpunkte, Administratoren, edid und Token früherer Überspring-Aktionen. Dieser GET reduziert den Zähler nicht; dafür PUT /list/skip_counter verwenden.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edsubscription_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":[]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^skip_[a-f0-9]{12}_-?[0-9]+$","examples":["skip_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"date_create":{"type":["string","boolean"],"description":"ISO-Datumswert aus dQ() oder false bei fehlendem Datum."},"admin_name":{"type":"string"},"edid":{"type":"string"},"token":{"type":"string"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^skip_[a-f0-9]{12}_-?[0-9]+$","examples":["skip_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"date_create":{"type":["string","boolean"],"description":"ISO-Datumswert aus dQ() oder false bei fehlendem Datum."},"admin_name":{"type":"string"},"edid":{"type":"string"},"token":{"type":"string"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/2","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":370,"reason":"APIPermissions resource mapping to edsu.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Lists the existing skip history; it does not consume or create a skip.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/2","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326,"branch":{"file":"application/models/v2/Edsubscription.php","line":347}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/list/skip_counter Verfügbare Zahlungssperre einmal überspringen Reduziert den Konto-Zähler um eins und protokolliert Administrator und Token, wenn noch Versuche vorhanden sind. data-Felder werden ignoriert. Bei erschöpftem Zähler bleibt update=false. - Operation-ID: put_v2_edsubscription_list_skip_counter - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-list-skip-counter-6c67d90bcc8d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {}, "id": "account" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/skip_counter' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/skip_counter"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {}, "id": "account" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/skip_counter" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{},\"id\":\"account\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_list_skip_counter","summary":"Verfügbare Zahlungssperre einmal überspringen","description":"Reduziert den Konto-Zähler um eins und protokolliert Administrator und Token, wenn noch Versuche vorhanden sind. data-Felder werden ignoriert. Bei erschöpftem Zähler bleibt update=false.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"skip_counter":1,"skip":"skip_012345abcdef_123456","token":"skip_synthetic_example"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","examples":["account_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"},"skip_counter":{"type":"integer"},"skip":{"type":"string","pattern":"^skip_[a-f0-9]{12}_-?[0-9]+$","examples":["skip_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"token":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Verfügbare Zahlungssperre einmal überspringen","value":{"data":{},"id":"account"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":251}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edsubscription/list/subscriptions_verification Tatsächliche Abonnementnutzung prüfen Liest aktuelle Nutzungszähler und aktivierte Funktionen zur Prüfung eines möglichen Planwechsels; ändert keinen Plan. - Operation-ID: get_v2_edsubscription_list_subscriptions_verification - Dokumentation: /docs/api/v2/operations/get-v2-edsubscription-list-subscriptions-verification-1ee55bd24db7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/subscriptions_verification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/subscriptions_verification"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/subscriptions_verification" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edsubscription_list_subscriptions_verification","summary":"Tatsächliche Abonnementnutzung prüfen","description":"Liest aktuelle Nutzungszähler und aktivierte Funktionen zur Prüfung eines möglichen Planwechsels; ändert keinen Plan.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offer_maximum":3,"user_maximum":20,"lesson_booking":false,"pricecategorie_maximum":1,"internal_function":false,"oauth2_provider":0,"datastructure_function":2,"edoobox_connect":0,"offerlist_maximum":0,"certificate_validation":false,"offer_signup_start":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"offer_maximum":{"type":"integer"},"user_maximum":{"type":"integer"},"pricecategorie_maximum":{"type":"integer"},"oauth2_provider":{"type":"integer"},"datastructure_function":{"type":"integer"},"edoobox_connect":{"type":"integer"},"offerlist_maximum":{"type":"integer"},"lesson_booking":{"type":"boolean"},"certificate_validation":{"type":"boolean"},"offer_signup_start":{"type":"boolean"},"internal_function":{}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/3","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":370,"reason":"APIPermissions resource mapping to edsu.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Computes subscription feature/usage verification from existing account data; it neither switches plans nor bills the account.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/3","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"getEdsubscription","line":326,"branch":{"file":"application/models/v2/Edsubscription.php","line":365}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/edsubscription/list/vat_relationship Geschäftsbeziehung B2B oder B2C festlegen Legt die Geschäftsbeziehung einmalig fest. B2B erfordert vat_number und eine erfolgreiche VIES-Prüfung, B2C keine Umsatzsteuer-ID. Bestehende Beziehungen sind hier nicht änderbar. B2B kann den gespeicherten Abopreis um enthaltene Steuer reduzieren. - Operation-ID: put_v2_edsubscription_list_vat_relationship - Dokumentation: /docs/api/v2/operations/put-v2-edsubscription-list-vat-relationship-8ce48f47ba95/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "relationship": "B2C" }, "id": "account" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/edsubscription/list/vat_relationship' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edsubscription/list/vat_relationship"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "relationship": "B2C" }, "id": "account" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edsubscription/list/vat_relationship" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"relationship\":\"B2C\"},\"id\":\"account\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_edsubscription_list_vat_relationship","summary":"Geschäftsbeziehung B2B oder B2C festlegen","description":"Legt die Geschäftsbeziehung einmalig fest. B2B erfordert vat_number und eine erfolgreiche VIES-Prüfung, B2C keine Umsatzsteuer-ID. Bestehende Beziehungen sind hier nicht änderbar. B2B kann den gespeicherten Abopreis um enthaltene Steuer reduzieren.","tags":["edsubscription"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","examples":["account_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"},"vat_valid":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"relationship":{"type":"string","enum":["B2B","B2C"]},"vat_number":{"type":"string"}},"additionalProperties":true,"required":["relationship"],"if":{"properties":{"relationship":{"const":"B2B"}}},"then":{"required":["vat_number"]}},"id":{"type":"string","description":"Legacy-Selektor: Bei /list-Schreibrouten benötigt der Dispatcher top-level id. Die Aktion verwendet anschließend das authentifizierte Konto; üblicher Wert account.","examples":["account"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Geschäftsbeziehung B2B oder B2C festlegen","value":{"data":{"relationship":"B2C"},"id":"account"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edsubscription.php","method":"updateEdsubscription","line":230,"branch":{"file":"application/models/v2/Edsubscription.php","line":258}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edsubscription","short":"edsu","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction edoobox-Kontobuchungen anzeigen Liest Gutschriften und Belastungen des edoobox-Kontos, einschließlich Abonnement, Zahlungen und Buchungsgebühren. Business-Beträge berücksichtigen die zugehörigen Buchungsgebühren. fields, filter, limit, order und response_type folgen dem allgemeinen Listenvertrag; auch eine ID-Selektion wird standardmäßig als ID-indizierte Sammlung ausgegeben. - Operation-ID: get_v2_edtransaction - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-23f23d3ea24b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction","summary":"edoobox-Kontobuchungen anzeigen","description":"Liest Gutschriften und Belastungen des edoobox-Kontos, einschließlich Abonnement, Zahlungen und Buchungsgebühren. Business-Beträge berücksichtigen die zugehörigen Buchungsgebühren. fields, filter, limit, order und response_type folgen dem allgemeinen Listenvertrag; auch eine ID-Selektion wird standardmäßig als ID-indizierte Sammlung ausgegeben.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von edoobox-Kontobuchungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edtransaction_012345abcdef_123456":{"id":"edtransaction_012345abcdef_123456","amount":-24.5,"currency":"CHF","description":"Beispielbelastung","date_accounting":"2026-10-15T10:00:00+02:00","type":"edsubscription"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["edtransaction_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":["string","null"]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["edtransaction_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":["string","null"]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste. Leere Resultate können als [] ausgegeben werden."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction/{id} edoobox-Kontobuchung prüfen Liest Gutschriften und Belastungen des edoobox-Kontos, einschließlich Abonnement, Zahlungen und Buchungsgebühren. Business-Beträge berücksichtigen die zugehörigen Buchungsgebühren. fields, filter, limit, order und response_type folgen dem allgemeinen Listenvertrag; auch eine ID-Selektion wird standardmäßig als ID-indizierte Sammlung ausgegeben. - Operation-ID: get_v2_edtransaction_id - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-id-f9151c1ab044/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edtransaction_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction_id","summary":"edoobox-Kontobuchung prüfen","description":"Liest Gutschriften und Belastungen des edoobox-Kontos, einschließlich Abonnement, Zahlungen und Buchungsgebühren. Business-Beträge berücksichtigen die zugehörigen Buchungsgebühren. fields, filter, limit, order und response_type folgen dem allgemeinen Listenvertrag; auch eine ID-Selektion wird standardmäßig als ID-indizierte Sammlung ausgegeben.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edtransaction_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von edoobox-Kontobuchungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edtransaction_012345abcdef_123456":{"id":"edtransaction_012345abcdef_123456","amount":-24.5,"currency":"CHF","description":"Beispielbelastung","date_accounting":"2026-10-15T10:00:00+02:00","type":"edsubscription"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["edtransaction_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":["string","null"]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["edtransaction_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":["string","null"]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste. Leere Resultate können als [] ausgegeben werden."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction/{id}/business_transaction Buchungsgebühren einer Kontobelastung aufschlüsseln Liefert die einzelnen Teilnehmerbuchungen einer Business-Kontobelastung mit Buchungsnummer, Zeitpunkt, fixen und prozentualen Kosten. id stammt aus dem edoobox-Kontoauszug; zusätzliche Listenparameter werden nicht angewandt. - Operation-ID: get_v2_edtransaction_id_business_transaction - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-id-business-transaction-483c5bfbfd3c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edtransaction_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction/'"${PATH_ID}"'/business_transaction' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction/{id}/business_transaction"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction/{id}/business_transaction" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction_id_business_transaction","summary":"Buchungsgebühren einer Kontobelastung aufschlüsseln","description":"Liefert die einzelnen Teilnehmerbuchungen einer Business-Kontobelastung mit Buchungsnummer, Zeitpunkt, fixen und prozentualen Kosten. id stammt aus dem edoobox-Kontoauszug; zusätzliche Listenparameter werden nicht angewandt.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edtransaction_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Liste der verursachenden Buchungen; leer, wenn keine zugeordnet sind.","content":{"application/json":{"example":{"success":true,"error":false,"data":[]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"time":{"anyOf":[{"type":"string"},{"const":false}]},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"template":{"type":"string"},"costs":{"type":"object","properties":{"amount":{"type":"number"},"percent":{"type":"object","properties":{"amount":{"type":"number"},"percentage":{"type":["number","string"]}},"additionalProperties":true},"currency":{"type":"string"}},"additionalProperties":true},"number":{"type":["string","integer"]}},"additionalProperties":true}},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205,"branch":{"file":"application/models/v2/Edtransaction.php","line":305}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction/{id}/calls_sms_letters_transaction Monatliche Nachrichtenkosten aufschlüsseln Liefert die Einzelkosten für Briefe, SMS und Anrufe eines Monats. Das Pfadsegment id ist hier der month-year-Wert aus dem Kontoauszug (MM-YYYY), keine v2-ID. Gibt Zeitpunkt, Typ, formatierten Text, Detail und Kosten aus. - Operation-ID: get_v2_edtransaction_id_calls_sms_letters_transaction - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-id-calls-sms-letters-transaction-793738828ab4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | month-year aus einer lettersmscall-Zeile des Kontoauszugs. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"month-year aus einer lettersmscall-Zeile des Kontoauszugs.","schema":{"type":"string","description":"Monat-Jahr, kanonisch MM-YYYY.","examples":["10-2026"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction/'"${PATH_ID}"'/calls_sms_letters_transaction' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction/{id}/calls_sms_letters_transaction"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction/{id}/calls_sms_letters_transaction" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction_id_calls_sms_letters_transaction","summary":"Monatliche Nachrichtenkosten aufschlüsseln","description":"Liefert die Einzelkosten für Briefe, SMS und Anrufe eines Monats. Das Pfadsegment id ist hier der month-year-Wert aus dem Kontoauszug (MM-YYYY), keine v2-ID. Gibt Zeitpunkt, Typ, formatierten Text, Detail und Kosten aus.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"month-year aus einer lettersmscall-Zeile des Kontoauszugs.","schema":{"type":"string","description":"Monat-Jahr, kanonisch MM-YYYY.","examples":["10-2026"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Liste der Nachrichtenkosten des gewählten Monats.","content":{"application/json":{"example":{"success":true,"error":false,"data":[]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"time":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"template":{"type":"string"},"detail":{"type":"string"},"costs":{"type":"object","properties":{"amount":{"type":"number"}},"additionalProperties":true}},"additionalProperties":true}},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205,"branch":{"file":"application/models/v2/Edtransaction.php","line":317}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction/{id}/transaction_invoice edoobox-Kontorechnung herunterladen Lädt die zum Kontobuchungseintrag verknüpfte edoobox-Rechnung als PDF. Ohne Verknüpfung antwortet der Endpunkt mit ED80100 und no_invoice_exist. - Operation-ID: get_v2_edtransaction_id_transaction_invoice - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-id-transaction-invoice-557f95a7e88c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edtransaction_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction/'"${PATH_ID}"'/transaction_invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction/{id}/transaction_invoice"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction/{id}/transaction_invoice" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction_id_transaction_invoice","summary":"edoobox-Kontorechnung herunterladen","description":"Lädt die zum Kontobuchungseintrag verknüpfte edoobox-Rechnung als PDF. Ohne Verknüpfung antwortet der Endpunkt mit ED80100 und no_invoice_exist.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edtransaction_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"PDF-Dokument; Fehler verwenden die JSON-Fehlerhülle.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true}},"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205,"branch":{"file":"application/models/v2/Edtransaction.php","line":338}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction/{id}/transaction_receipt Zahlungsbeleg zum edoobox-Konto laden Erzeugt einen PDF-Beleg für eine unterstützte Zahlungs-Kontobuchung (Rechnung, PayPal, Sofort, Karte, Saferpay oder Bitpay). Fehlender oder ungeeigneter Eintrag liefert ED80100, gegebenenfalls no_receipt_exist. - Operation-ID: get_v2_edtransaction_id_transaction_receipt - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-id-transaction-receipt-0bf437cbb6a7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edtransaction_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction/'"${PATH_ID}"'/transaction_receipt' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction/{id}/transaction_receipt"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction/{id}/transaction_receipt" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction_id_transaction_receipt","summary":"Zahlungsbeleg zum edoobox-Konto laden","description":"Erzeugt einen PDF-Beleg für eine unterstützte Zahlungs-Kontobuchung (Rechnung, PayPal, Sofort, Karte, Saferpay oder Bitpay). Fehlender oder ungeeigneter Eintrag liefert ED80100, gegebenenfalls no_receipt_exist.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["edtransaction_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"PDF-Dokument; Fehler verwenden die JSON-Fehlerhülle.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true}},"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205,"branch":{"file":"application/models/v2/Edtransaction.php","line":325}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction/list edoobox-Kontobuchungen anzeigen Liest Gutschriften und Belastungen des edoobox-Kontos, einschließlich Abonnement, Zahlungen und Buchungsgebühren. Business-Beträge berücksichtigen die zugehörigen Buchungsgebühren. fields, filter, limit, order und response_type folgen dem allgemeinen Listenvertrag; auch eine ID-Selektion wird standardmäßig als ID-indizierte Sammlung ausgegeben. - Operation-ID: get_v2_edtransaction_list - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-list-aa02ca9e1e9c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction_list","summary":"edoobox-Kontobuchungen anzeigen","description":"Liest Gutschriften und Belastungen des edoobox-Kontos, einschließlich Abonnement, Zahlungen und Buchungsgebühren. Business-Beträge berücksichtigen die zugehörigen Buchungsgebühren. fields, filter, limit, order und response_type folgen dem allgemeinen Listenvertrag; auch eine ID-Selektion wird standardmäßig als ID-indizierte Sammlung ausgegeben.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"edtransaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von edoobox-Kontobuchungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"edtransaction_012345abcdef_123456":{"id":"edtransaction_012345abcdef_123456","amount":-24.5,"currency":"CHF","description":"Beispielbelastung","date_accounting":"2026-10-15T10:00:00+02:00","type":"edsubscription"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["edtransaction_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":["string","null"]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["edtransaction_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":["string","null"]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste. Leere Resultate können als [] ausgegeben werden."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction/list/balance edoobox-Kontostand zum Stichtag berechnen Berechnet den Saldo der Kontobuchungen vor date_accounting (Standard jetzt) plus noch separat erfasste Buchungs- sowie Nachrichtenkosten. Nur der date_accounting-Filter wird ausgewertet; keine Pagination oder Feldprojektion. - Operation-ID: get_v2_edtransaction_list_balance - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-list-balance-12b2dff7f9ae/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | Optionaler date_accounting-Filter bestimmt den Stichtag (strtotime-Text); Standard jetzt. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"Optionaler date_accounting-Filter bestimmt den Stichtag (strtotime-Text); Standard jetzt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_accounting","value":"2026-10-15T00:00:00+02:00"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction/list/balance' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction/list/balance"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction/list/balance" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction_list_balance","summary":"edoobox-Kontostand zum Stichtag berechnen","description":"Berechnet den Saldo der Kontobuchungen vor date_accounting (Standard jetzt) plus noch separat erfasste Buchungs- sowie Nachrichtenkosten. Nur der date_accounting-Filter wird ausgewertet; keine Pagination oder Feldprojektion.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"Optionaler date_accounting-Filter bestimmt den Stichtag (strtotime-Text); Standard jetzt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"date_accounting","value":"2026-10-15T00:00:00+02:00"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Objekt mit numerischem balance.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"balance":125.5}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"balance":{"type":"number"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/5","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":430,"reason":"APIPermissions resource mapping to edtr.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Computes account balance from SUMs of existing ledger, booking-cost and communication-cost rows.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/5","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205,"branch":{"file":"application/models/v2/Edtransaction.php","line":213}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/edtransaction/list/table Monatlichen edoobox-Kontoauszug anzeigen Gruppiert sämtliche Kontobuchungen nach period_MM_YY, neueste Monate und Buchungen zuerst. Enthält Monatsüberschriften, laufende Salden, financial_summary und verfügbare Rechnungsbelege. Briefe/SMS/Anrufe werden monatlich zusammengefasst; solche Zeilen und Plugin-Zeilen besitzen id=false und einen month-year-Selektor. Die allgemeinen Filter werden nicht ausgewertet. - Operation-ID: get_v2_edtransaction_list_table - Dokumentation: /docs/api/v2/operations/get-v2-edtransaction-list-table-46e78ca71416/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/edtransaction/list/table' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/edtransaction/list/table"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/edtransaction/list/table" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_edtransaction_list_table","summary":"Monatlichen edoobox-Kontoauszug anzeigen","description":"Gruppiert sämtliche Kontobuchungen nach period_MM_YY, neueste Monate und Buchungen zuerst. Enthält Monatsüberschriften, laufende Salden, financial_summary und verfügbare Rechnungsbelege. Briefe/SMS/Anrufe werden monatlich zusammengefasst; solche Zeilen und Plugin-Zeilen besitzen id=false und einen month-year-Selektor. Die allgemeinen Filter werden nicht ausgewertet.","tags":["edtransaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Monatsweise indiziertes Objekt oder [] bei leerem Konto.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"period_10_26":{"data":[{"date_accounting":"2026-10-15T10:00:00+02:00","name":"Beispielbelastung","credits":0,"debits":-24.5,"balance":125.5,"type":"edsubscription","plugins":[],"id":"edtransaction_012345abcdef_123456","invoice_available":false,"invoice_reference":false}],"financial_summary":{"debits":-24.5,"credits":0,"balance":125.5},"name":"1. – 31 Oktober 2026"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"name":{"type":"string"},"credits":{"type":"number"},"debits":{"type":"number"},"balance":{"type":"number"},"type":{"type":"string"},"plugins":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"}},"additionalProperties":true}},"month-year":{"type":"string"},"id":{"anyOf":[{"type":"string","pattern":"^edtransaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["edtransaction_012345abcdef_123456"]},{"const":false}]},"invoice_available":{"type":"boolean"},"invoice_reference":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},"financial_summary":{"type":"object","properties":{"debits":{"type":"number"},"credits":{"type":"number"},"balance":{"type":"number"}},"additionalProperties":true},"name":{"type":"string"}},"additionalProperties":true}},{"type":"array","maxItems":0}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/6","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":430,"reason":"APIPermissions resource mapping to edtr.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Builds the account statement table from existing transaction rows and optional existing plugin billing labels.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/6","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Edtransaction.php","method":"getEdtransaction","line":205,"branch":{"file":"application/models/v2/Edtransaction.php","line":210}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"edtransaction","short":"edtr","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate E-Mail- und PDF-Vorlagen auflisten Liest gespeicherte Sprachfassungen von E-Mail- und PDF-Vorlagen. id bezeichnet die einzelne Sprachfassung (et_), template die Vorlagenfamilie (emailtemplate_). type und compatibility sind unveränderliche Familienmerkmale. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_emailtemplate - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-6384f83a581a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate","summary":"E-Mail- und PDF-Vorlagen auflisten","description":"Liest gespeicherte Sprachfassungen von E-Mail- und PDF-Vorlagen. id bezeichnet die einzelne Sprachfassung (et_), template die Vorlagenfamilie (emailtemplate_). type und compatibility sind unveränderliche Familienmerkmale. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"et_012345abcdef_123456":{"id":"et_012345abcdef_123456","template":"emailtemplate_012345abcdef_123456","type":"email","compatibility":null,"language":"de","template_name":"Kursinformation","subject":"Information zu Ihrem Kurs","html":"

Herzlich willkommen.

","status":true,"template_account":true,"isLUT":false,"pdf":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/emailtemplate E-Mail- oder PDF-Vorlage in einer Sprache anlegen Erstellt eine einzelne Sprachfassung einer existierenden Vorlagenfamilie oder mit template=new eine neue eigene Familie. template, language, subject und html sind Pflicht. Eine Familie/Sprache darf nur einmal existieren. type und compatibility werden bei neuen Familien festgelegt und sind danach unveränderlich; eigene PDF-Familien erhalten participant_document. Antwort-ID ist et_, eine neue Familien-ID emailtemplate_. Dieser Endpunkt verarbeitet vor dem generischen Writer einzelne Objekte; kein Batch-Umschlag. - Operation-ID: post_v2_emailtemplate - Dokumentation: /docs/api/v2/operations/post-v2-emailtemplate-745a478fde60/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "template": "new", "type": "email", "language": "de", "template_name": "Kursinformation", "subject": "Information zu Ihrem Kurs", "html": "

Herzlich willkommen.

", "status": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "template": "new", "type": "email", "language": "de", "template_name": "Kursinformation", "subject": "Information zu Ihrem Kurs", "html": "

Herzlich willkommen.

", "status": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"template\":\"new\",\"type\":\"email\",\"language\":\"de\",\"template_name\":\"Kursinformation\",\"subject\":\"Information zu Ihrem Kurs\",\"html\":\"

Herzlich willkommen.

\",\"status\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_emailtemplate","summary":"E-Mail- oder PDF-Vorlage in einer Sprache anlegen","description":"Erstellt eine einzelne Sprachfassung einer existierenden Vorlagenfamilie oder mit template=new eine neue eigene Familie. template, language, subject und html sind Pflicht. Eine Familie/Sprache darf nur einmal existieren. type und compatibility werden bei neuen Familien festgelegt und sind danach unveränderlich; eigene PDF-Familien erhalten participant_document. Antwort-ID ist et_, eine neue Familien-ID emailtemplate_. Dieser Endpunkt verarbeitet vor dem generischen Writer einzelne Objekte; kein Batch-Umschlag.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue et_-ID und insert; template ist bei einer neu angelegten Familie zusätzlich enthalten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"et_012345abcdef_123456","insert":true,"template":"emailtemplate_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"insert":{"type":"boolean"},"template":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"template":{"anyOf":[{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"const":"new"}]},"type":{"type":"string","description":"email oder pdf; Kleinschreibung und Leerraum werden normalisiert. Bei neuer Familie standardmäßig email."},"compatibility":{"type":["string","null"],"description":"Bei neuer PDF-Familie participant_document, bei neuer E-Mail-Familie null. Für zusätzliche Sprachfassungen werden die Familienwerte übernommen."},"language":{"type":"string","description":"Unterstützter edoobox-Sprachcode, z.B. de."},"template_name":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 50 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Kursinformation"]},"subject":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Information zu Ihrem Kurs"]},"html":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 65500 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["

Herzlich willkommen.

"]},"status":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]}},"additionalProperties":true,"required":["template","language","subject","html"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"E-Mail- oder PDF-Vorlage in einer Sprache anlegen","value":{"data":{"template":"new","type":"email","language":"de","template_name":"Kursinformation","subject":"Information zu Ihrem Kurs","html":"

Herzlich willkommen.

","status":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"postEmailtemplate","line":184},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/emailtemplate Inhalt einer Vorlagen-Sprachfassung ändern Ändert Betreff, HTML, Anzeigename oder Aktivierung einer gespeicherten Sprachfassung. Vorlage, Sprache, type und compatibility bleiben unveränderlich; abweichende Metadaten werden explizit zurückgewiesen. HTML für PDF-Vorlagen schreibt Dateiverweise auf lokale Renderpfade um. id im JSON-Umschlag ist Pflicht. - Operation-ID: put_v2_emailtemplate - Dokumentation: /docs/api/v2/operations/put-v2-emailtemplate-cf7f6a6b981b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "subject": "Aktualisierte Kursinformation", "html": "

Vielen Dank für Ihre Anmeldung.

" }, "id": "et_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "subject": "Aktualisierte Kursinformation", "html": "

Vielen Dank für Ihre Anmeldung.

" }, "id": "et_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"subject\":\"Aktualisierte Kursinformation\",\"html\":\"

Vielen Dank für Ihre Anmeldung.

\"},\"id\":\"et_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_emailtemplate","summary":"Inhalt einer Vorlagen-Sprachfassung ändern","description":"Ändert Betreff, HTML, Anzeigename oder Aktivierung einer gespeicherten Sprachfassung. Vorlage, Sprache, type und compatibility bleiben unveränderlich; abweichende Metadaten werden explizit zurückgewiesen. HTML für PDF-Vorlagen schreibt Dateiverweise auf lokale Renderpfade um. id im JSON-Umschlag ist Pflicht.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche et_-ID und tatsächlicher Änderungsstatus.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"et_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"template_name":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 50 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Kursinformation"]},"subject":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Information zu Ihrem Kurs"]},"html":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 65500 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["

Herzlich willkommen.

"]},"status":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"type":{"type":"string","description":"email oder pdf; Kleinschreibung und Leerraum werden normalisiert. Bei neuer Familie standardmäßig email."},"compatibility":{"type":["string","null"],"description":"Bei neuer PDF-Familie participant_document, bei neuer E-Mail-Familie null. Für zusätzliche Sprachfassungen werden die Familienwerte übernommen."}},"additionalProperties":true},"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Inhalt einer Vorlagen-Sprachfassung ändern","value":{"data":{"subject":"Aktualisierte Kursinformation","html":"

Vielen Dank für Ihre Anmeldung.

"},"id":"et_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"updateEmailtemplate","line":280},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/{id} Vorlage lesen Liest gespeicherte Sprachfassungen von E-Mail- und PDF-Vorlagen. id bezeichnet die einzelne Sprachfassung (et_), template die Vorlagenfamilie (emailtemplate_). type und compatibility sind unveränderliche Familienmerkmale. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_emailtemplate_id - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-id-d1bcdb7630c0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche et_-ID der Sprachfassung. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche et_-ID der Sprachfassung.","schema":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["et_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_id","summary":"Vorlage lesen","description":"Liest gespeicherte Sprachfassungen von E-Mail- und PDF-Vorlagen. id bezeichnet die einzelne Sprachfassung (et_), template die Vorlagenfamilie (emailtemplate_). type und compatibility sind unveränderliche Familienmerkmale. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche et_-ID der Sprachfassung.","schema":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["et_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"et_012345abcdef_123456":{"id":"et_012345abcdef_123456","template":"emailtemplate_012345abcdef_123456","type":"email","compatibility":null,"language":"de","template_name":"Kursinformation","subject":"Information zu Ihrem Kurs","html":"

Herzlich willkommen.

","status":true,"template_account":true,"isLUT":false,"pdf":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/emailtemplate/{id} Inhalt einer Vorlagen-Sprachfassung ändern Ändert Betreff, HTML, Anzeigename oder Aktivierung einer gespeicherten Sprachfassung. Vorlage, Sprache, type und compatibility bleiben unveränderlich; abweichende Metadaten werden explizit zurückgewiesen. HTML für PDF-Vorlagen schreibt Dateiverweise auf lokale Renderpfade um. Die ID im Pfad ist die et_-ID, nicht die emailtemplate_-Familie. - Operation-ID: put_v2_emailtemplate_id - Dokumentation: /docs/api/v2/operations/put-v2-emailtemplate-id-6a6781ff0956/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche et_-ID der Sprachfassung. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche et_-ID der Sprachfassung.","schema":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["et_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "subject": "Aktualisierte Kursinformation", "html": "

Vielen Dank für Ihre Anmeldung.

" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "subject": "Aktualisierte Kursinformation", "html": "

Vielen Dank für Ihre Anmeldung.

" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"subject\":\"Aktualisierte Kursinformation\",\"html\":\"

Vielen Dank für Ihre Anmeldung.

\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_emailtemplate_id","summary":"Inhalt einer Vorlagen-Sprachfassung ändern","description":"Ändert Betreff, HTML, Anzeigename oder Aktivierung einer gespeicherten Sprachfassung. Vorlage, Sprache, type und compatibility bleiben unveränderlich; abweichende Metadaten werden explizit zurückgewiesen. HTML für PDF-Vorlagen schreibt Dateiverweise auf lokale Renderpfade um. Die ID im Pfad ist die et_-ID, nicht die emailtemplate_-Familie.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche et_-ID der Sprachfassung.","schema":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["et_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche et_-ID und tatsächlicher Änderungsstatus.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"et_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"template_name":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 50 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Kursinformation"]},"subject":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Information zu Ihrem Kurs"]},"html":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 65500 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["

Herzlich willkommen.

"]},"status":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"type":{"type":"string","description":"email oder pdf; Kleinschreibung und Leerraum werden normalisiert. Bei neuer Familie standardmäßig email."},"compatibility":{"type":["string","null"],"description":"Bei neuer PDF-Familie participant_document, bei neuer E-Mail-Familie null. Für zusätzliche Sprachfassungen werden die Familienwerte übernommen."}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Inhalt einer Vorlagen-Sprachfassung ändern","value":{"data":{"subject":"Aktualisierte Kursinformation","html":"

Vielen Dank für Ihre Anmeldung.

"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"updateEmailtemplate","line":280},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/{id}/attachment_url Signierte Lese-URL einer Vorlagendatei abrufen Liefert Metadaten und eine signierte Lese-URL für einen bereits verifizierten Anhang oder PDF-Hintergrund des Kontos. Die ID bezeichnet attachment_ oder pdftemplate_. Die Aktion lädt keine Datei hoch und ändert keine Vorlagen. Die öffentliche Datei-ID im Pfad wird als ID-Filter verwendet. - Operation-ID: get_v2_emailtemplate_id_attachment_url - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-id-attachment-url-ee9afda5b1c6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Datei-ID. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Datei-ID.","schema":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'/attachment_url' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}/attachment_url"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}/attachment_url" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_id_attachment_url","summary":"Signierte Lese-URL einer Vorlagendatei abrufen","description":"Liefert Metadaten und eine signierte Lese-URL für einen bereits verifizierten Anhang oder PDF-Hintergrund des Kontos. Die ID bezeichnet attachment_ oder pdftemplate_. Die Aktion lädt keine Datei hoch und ändert keine Vorlagen. Die öffentliche Datei-ID im Pfad wird als ID-Filter verwendet.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Datei-ID.","schema":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nach öffentlicher Datei-ID indiziertes Objekt; das URL-Feld heißt für beide Dateitypen attachment_url.","content":{"application/json":{"schema":{"type":"object","required":["data","error"],"properties":{"data":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer"},"last_change":{"type":"string","format":"date-time"},"attachment_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["id","emailtemplate","file_name","file_size","last_change","attachment_url"]}},"error":{"const":false},"type":{"type":"string"},"success":{"const":true}},"additionalProperties":true},"example":{"data":{"attachment_012345abcdef_123456":{"id":"attachment_012345abcdef_123456","emailtemplate":"et_012345abcdef_123456","file_name":"Information.pdf","file_size":1200,"last_change":"2026-10-22T10:00:00+00:00","attachment_url":"https://example.org/signed-download"}},"error":false,"type":"admin","success":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-user-handoff":{"sensitivePaths":["/data"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/*/attachment_url"],"x-edoobox-user-handoff-source":{"file":"application/models/v2/Emailtemplatefunc.php","method":"_showPDFAttachment","line":554,"lineEnd":642,"reason":"The typed child lookup is constrained to an account-owned parent, rejects pending objects, and returns a signed storage URL alongside child/parent v2 IDs and file metadata. Deliver the complete data map only through the protected one-time authenticated human handoff. Signed URL, signature and legacy object path must not enter model results, previews, logs or normal result caches. Upstream URL lifetime is unchanged.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":337}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/{id}/pdf_template_url Signierte Lese-URL einer Vorlagendatei abrufen Liefert Metadaten und eine signierte Lese-URL für einen bereits verifizierten Anhang oder PDF-Hintergrund des Kontos. Die ID bezeichnet attachment_ oder pdftemplate_. Die Aktion lädt keine Datei hoch und ändert keine Vorlagen. Die öffentliche Datei-ID im Pfad wird als ID-Filter verwendet. - Operation-ID: get_v2_emailtemplate_id_pdf_template_url - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-id-pdf-template-url-ec10f9dce8b6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche Datei-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"description":"Öffentliche Datei-ID.","schema":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'/pdf_template_url' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}/pdf_template_url"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}/pdf_template_url" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_id_pdf_template_url","summary":"Signierte Lese-URL einer Vorlagendatei abrufen","description":"Liefert Metadaten und eine signierte Lese-URL für einen bereits verifizierten Anhang oder PDF-Hintergrund des Kontos. Die ID bezeichnet attachment_ oder pdftemplate_. Die Aktion lädt keine Datei hoch und ändert keine Vorlagen. Die öffentliche Datei-ID im Pfad wird als ID-Filter verwendet.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"description":"Öffentliche Datei-ID.","schema":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nach öffentlicher Datei-ID indiziertes Objekt; das URL-Feld heißt für beide Dateitypen attachment_url.","content":{"application/json":{"schema":{"type":"object","required":["data","error"],"properties":{"data":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer"},"last_change":{"type":"string","format":"date-time"},"attachment_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["id","emailtemplate","file_name","file_size","last_change","attachment_url"]}},"error":{"const":false},"type":{"type":"string"},"success":{"const":true}},"additionalProperties":true},"example":{"data":{"attachment_012345abcdef_123456":{"id":"attachment_012345abcdef_123456","emailtemplate":"et_012345abcdef_123456","file_name":"Information.pdf","file_size":1200,"last_change":"2026-10-22T10:00:00+00:00","attachment_url":"https://example.org/signed-download"}},"error":false,"type":"admin","success":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-user-handoff":{"sensitivePaths":["/data"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/*/attachment_url"],"x-edoobox-user-handoff-source":{"file":"application/models/v2/Emailtemplatefunc.php","method":"_showPDFAttachment","line":554,"lineEnd":642,"reason":"The typed child lookup is constrained to an account-owned parent, rejects pending objects, and returns a signed storage URL alongside child/parent v2 IDs and file metadata. Deliver the complete data map only through the protected one-time authenticated human handoff. Signed URL, signature and legacy object path must not enter model results, previews, logs or normal result caches. Upstream URL lifetime is unchanged.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":345}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/{id}/placeholders Platzhalter einer Vorlagenfamilie lesen Liefert erlaubte Platzhalter gruppiert nach aktueller Vorlage, Buchung, Angebot, Teilnehmer und Organisation. id ist eine emailtemplate_-Familien-ID (auch Systemfamilien), keine et_-Sprachfassungs-ID. Leere Gruppen werden ausgelassen. - Operation-ID: get_v2_emailtemplate_id_placeholders - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-id-placeholders-484c19ef2faf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Vorlagenfamilie. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Vorlagenfamilie.","schema":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["emailtemplate_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'/placeholders' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}/placeholders"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}/placeholders" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_id_placeholders","summary":"Platzhalter einer Vorlagenfamilie lesen","description":"Liefert erlaubte Platzhalter gruppiert nach aktueller Vorlage, Buchung, Angebot, Teilnehmer und Organisation. id ist eine emailtemplate_-Familien-ID (auch Systemfamilien), keine et_-Sprachfassungs-ID. Leere Gruppen werden ausgelassen.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Vorlagenfamilie.","schema":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["emailtemplate_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Liste nichtleerer Gruppen mit Schlüssel, übersetztem Namen und Platzhalternamen.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"key":"offer","name":"Angebot","placeholders":["OfferName","OfferStartDate"]}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"key":{"enum":["template_specific","booking","offer","user","company"]},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/11","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":358,"reason":"APIPermissions resource mapping to emtp.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Returns available placeholder names for an existing template family; it does not execute or send a template.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/11","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":353}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/{id}/preview Vorlage mit Beispieldaten als HTML oder PDF rendern Rendert eine gespeicherte et_-Sprachfassung mit synthetischen Beispieldaten. Beide Aktionsnamen verwenden dieselbe Implementierung; der gespeicherte Vorlagentyp entscheidet über HTML oder PDF. language-Header ist Pflicht. E-Mail-Vorlagen liefern JSON mit id, subject und html. PDF-Vorlagen liefern mit pdf=base64 JSON mit id und pdf; sonst PDF-Bytes direkt. Es werden keine E-Mails versendet und keine Vorlage gespeichert. - Operation-ID: get_v2_emailtemplate_id_preview - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-id-preview-f1b320d34886/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche et_-ID der Kontosprachfassung. | | pdf | query | nein | Bei PDF-Vorlagen base64 für JSON, sonst direkte PDF-Ausgabe. | | language | header | ja | Sprachcode für die Beispieldaten, z.B. de. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche et_-ID der Kontosprachfassung.","schema":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["et_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Bei PDF-Vorlagen base64 für JSON, sonst direkte PDF-Ausgabe.","schema":{"type":"string","enum":["base64","file"],"default":"file"}},{"name":"language","in":"header","required":true,"description":"Sprachcode für die Beispieldaten, z.B. de.","schema":{"type":"string"}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${HEADER_LANGUAGE:?HEADER_LANGUAGE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'/preview' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'language: '"${HEADER_LANGUAGE}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}/preview"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers["language"] = "" + required("HEADER_LANGUAGE"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}/preview" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["language"] = "" + os.environ["HEADER_LANGUAGE"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_id_preview","summary":"Vorlage mit Beispieldaten als HTML oder PDF rendern","description":"Rendert eine gespeicherte et_-Sprachfassung mit synthetischen Beispieldaten. Beide Aktionsnamen verwenden dieselbe Implementierung; der gespeicherte Vorlagentyp entscheidet über HTML oder PDF. language-Header ist Pflicht. E-Mail-Vorlagen liefern JSON mit id, subject und html. PDF-Vorlagen liefern mit pdf=base64 JSON mit id und pdf; sonst PDF-Bytes direkt. Es werden keine E-Mails versendet und keine Vorlage gespeichert.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche et_-ID der Kontosprachfassung.","schema":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["et_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Bei PDF-Vorlagen base64 für JSON, sonst direkte PDF-Ausgabe.","schema":{"type":"string","enum":["base64","file"],"default":"file"}},{"name":"language","in":"header","required":true,"description":"Sprachcode für die Beispieldaten, z.B. de.","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON für E-Mail-Vorschau oder Base64-PDF; andernfalls direktes PDF-Dokument. Fehler werden als JSON zurückgegeben.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"subject":{"type":"string"},"html":{"type":"string"}},"additionalProperties":true,"required":["id","subject","html"]},{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"pdf":{"type":"string","contentEncoding":"base64"}},"additionalProperties":true,"required":["id","pdf"]}]}},"additionalProperties":true},"example":{"success":true,"error":false,"data":{"id":"et_012345abcdef_123456","subject":"Kursinformation","html":"

Beispielvorschau

"}}},"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/12","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":358,"reason":"APIPermissions resource mapping to emtp.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Renders a preview against dummy content, returning HTML or a generated PDF. It does not save the content, send mail, create bookings or issue legal documents.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/12","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":439}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/{id}/previewPDF Vorlage mit Beispieldaten als HTML oder PDF rendern Rendert eine gespeicherte et_-Sprachfassung mit synthetischen Beispieldaten. Beide Aktionsnamen verwenden dieselbe Implementierung; der gespeicherte Vorlagentyp entscheidet über HTML oder PDF. language-Header ist Pflicht. E-Mail-Vorlagen liefern JSON mit id, subject und html. PDF-Vorlagen liefern mit pdf=base64 JSON mit id und pdf; sonst PDF-Bytes direkt. Es werden keine E-Mails versendet und keine Vorlage gespeichert. - Operation-ID: get_v2_emailtemplate_id_previewPDF - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-id-previewpdf-122f4a5aa87f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche et_-ID der Kontosprachfassung. | | pdf | query | nein | Bei PDF-Vorlagen base64 für JSON, sonst direkte PDF-Ausgabe. | | language | header | ja | Sprachcode für die Beispieldaten, z.B. de. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche et_-ID der Kontosprachfassung.","schema":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["et_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Bei PDF-Vorlagen base64 für JSON, sonst direkte PDF-Ausgabe.","schema":{"type":"string","enum":["base64","file"],"default":"file"}},{"name":"language","in":"header","required":true,"description":"Sprachcode für die Beispieldaten, z.B. de.","schema":{"type":"string"}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${HEADER_LANGUAGE:?HEADER_LANGUAGE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'/previewPDF' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'language: '"${HEADER_LANGUAGE}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}/previewPDF"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers["language"] = "" + required("HEADER_LANGUAGE"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}/previewPDF" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["language"] = "" + os.environ["HEADER_LANGUAGE"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_id_previewPDF","summary":"Vorlage mit Beispieldaten als HTML oder PDF rendern","description":"Rendert eine gespeicherte et_-Sprachfassung mit synthetischen Beispieldaten. Beide Aktionsnamen verwenden dieselbe Implementierung; der gespeicherte Vorlagentyp entscheidet über HTML oder PDF. language-Header ist Pflicht. E-Mail-Vorlagen liefern JSON mit id, subject und html. PDF-Vorlagen liefern mit pdf=base64 JSON mit id und pdf; sonst PDF-Bytes direkt. Es werden keine E-Mails versendet und keine Vorlage gespeichert.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche et_-ID der Kontosprachfassung.","schema":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["et_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Bei PDF-Vorlagen base64 für JSON, sonst direkte PDF-Ausgabe.","schema":{"type":"string","enum":["base64","file"],"default":"file"}},{"name":"language","in":"header","required":true,"description":"Sprachcode für die Beispieldaten, z.B. de.","schema":{"type":"string"}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"JSON für E-Mail-Vorschau oder Base64-PDF; andernfalls direktes PDF-Dokument. Fehler werden als JSON zurückgegeben.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"subject":{"type":"string"},"html":{"type":"string"}},"additionalProperties":true,"required":["id","subject","html"]},{"type":"object","properties":{"id":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"pdf":{"type":"string","contentEncoding":"base64"}},"additionalProperties":true,"required":["id","pdf"]}]}},"additionalProperties":true},"example":{"success":true,"error":false,"data":{"id":"et_012345abcdef_123456","subject":"Kursinformation","html":"

Beispielvorschau

"}}},"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":438}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/emailtemplate/{id}/remove_object PDF-Datei und Upload-Reservierung entfernen Löscht die hochgeladenen Bytes und anschließend die Reservierung bzw. den Dateieintrag. Auch ein noch unbestätigter Upload kann entfernt werden. Bei Storage-Fehlern bleibt die Reservierung für einen ausdrücklich gestarteten neuen Versuch erhalten. Die ID im Pfad ist attachment_ oder pdftemplate_, keine et_-ID. Kein JSON-Body erforderlich. - Operation-ID: delete_v2_emailtemplate_id_remove_object - Dokumentation: /docs/api/v2/operations/delete-v2-emailtemplate-id-remove-object-9683ca132ba1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche ID des Anhangs oder PDF-Hintergrunds. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche ID des Anhangs oder PDF-Hintergrunds.","schema":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'/remove_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}/remove_object"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}/remove_object" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_emailtemplate_id_remove_object","summary":"PDF-Datei und Upload-Reservierung entfernen","description":"Löscht die hochgeladenen Bytes und anschließend die Reservierung bzw. den Dateieintrag. Auch ein noch unbestätigter Upload kann entfernt werden. Bei Storage-Fehlern bleibt die Reservierung für einen ausdrücklich gestarteten neuen Versuch erhalten. Die ID im Pfad ist attachment_ oder pdftemplate_, keine et_-ID. Kein JSON-Body erforderlich.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche ID des Anhangs oder PDF-Hintergrunds.","schema":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Datei-ID und delete=true nach erfolgreicher Löschung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attachment_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"PDF-Datei und Upload-Reservierung entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"deleteEmailtemplate","line":308,"branch":{"file":"application/models/v2/Emailtemplate.php","line":314}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/emailtemplate/{id}/verify_object PDF-Upload prüfen und freigeben Prüft den gespeicherten Upload auf Existenz, MIME application/pdf und Byte-Grenzen (>20 und <1500000 für attachment, <1000000 für pdftemplate). PDF-Hintergründe müssen vom PDF/A-kompatiblen Import verarbeitet werden können. Ungültige Größe/Format/Import führt zur Löschung der hochgeladenen Bytes; bei Storage-Transportfehlern bleibt die Reservierung erhalten. Bereits bestätigte Uploads werden abgelehnt. Erfolg setzt verify=true. Die ID im Pfad ist attachment_ oder pdftemplate_, keine et_-ID. Kein JSON-Body erforderlich. - Operation-ID: put_v2_emailtemplate_id_verify_object - Dokumentation: /docs/api/v2/operations/put-v2-emailtemplate-id-verify-object-90ceb0770062/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche ID des Anhangs oder PDF-Hintergrunds. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche ID des Anhangs oder PDF-Hintergrunds.","schema":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/'"${PATH_ID}"'/verify_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/{id}/verify_object"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "PUT", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/{id}/verify_object" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_emailtemplate_id_verify_object","summary":"PDF-Upload prüfen und freigeben","description":"Prüft den gespeicherten Upload auf Existenz, MIME application/pdf und Byte-Grenzen (>20 und <1500000 für attachment, <1000000 für pdftemplate). PDF-Hintergründe müssen vom PDF/A-kompatiblen Import verarbeitet werden können. Ungültige Größe/Format/Import führt zur Löschung der hochgeladenen Bytes; bei Storage-Transportfehlern bleibt die Reservierung erhalten. Bereits bestätigte Uploads werden abgelehnt. Erfolg setzt verify=true. Die ID im Pfad ist attachment_ oder pdftemplate_, keine et_-ID. Kein JSON-Body erforderlich.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche ID des Anhangs oder PDF-Hintergrunds.","schema":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Datei-ID und Verifizierungs-/Änderungsstatus.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attachment_012345abcdef_123456","verify":true,"update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"verify":{"type":"boolean"},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","verify","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Emailtemplatefunc.php","line":361,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","method":"_verifyObject"},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"updateEmailtemplate","line":280,"branch":{"file":"application/models/v2/Emailtemplate.php","line":287}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/emailtemplate/attachment/add_object PDF-Datei an einer Vorlage zum Upload reservieren Reserviert einen PDF-Anhang (type=attachment) oder PDF-Hintergrund (type=pdftemplate) und liefert eine signierte HTTPS-Upload-URL. Der Typ wird aus data.type gelesen, unabhängig vom Segment vor add_object. emailtemplate ist die et_-ID einer vorhandenen Kontovorlage. Anhänge sind nur an E-Mail-Vorlagen erlaubt, höchstens fünf einschließlich offener Reservierungen; PDF-Hintergründe nur an PDF-Vorlagen und höchstens einer. Subscription muss Vorlagen erlauben. Datei anschließend an upload_url hochladen und verify_object aufrufen. file_name endet auf .pdf und ist einschließlich DB-Escaping höchstens 150 Zeichen; tatsächliche Datei wird erst bei verify geprüft. - Operation-ID: post_v2_emailtemplate_attachment_add_object - Dokumentation: /docs/api/v2/operations/post-v2-emailtemplate-attachment-add-object-e355b3d24730/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "emailtemplate": "et_012345abcdef_123456", "type": "attachment", "file_name": "Information.pdf", "file_type": "application/pdf", "file_size": 1200 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/attachment/add_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/attachment/add_object"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "emailtemplate": "et_012345abcdef_123456", "type": "attachment", "file_name": "Information.pdf", "file_type": "application/pdf", "file_size": 1200 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/attachment/add_object" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"emailtemplate\":\"et_012345abcdef_123456\",\"type\":\"attachment\",\"file_name\":\"Information.pdf\",\"file_type\":\"application/pdf\",\"file_size\":1200}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_emailtemplate_attachment_add_object","summary":"PDF-Datei an einer Vorlage zum Upload reservieren","description":"Reserviert einen PDF-Anhang (type=attachment) oder PDF-Hintergrund (type=pdftemplate) und liefert eine signierte HTTPS-Upload-URL. Der Typ wird aus data.type gelesen, unabhängig vom Segment vor add_object. emailtemplate ist die et_-ID einer vorhandenen Kontovorlage. Anhänge sind nur an E-Mail-Vorlagen erlaubt, höchstens fünf einschließlich offener Reservierungen; PDF-Hintergründe nur an PDF-Vorlagen und höchstens einer. Subscription muss Vorlagen erlauben. Datei anschließend an upload_url hochladen und verify_object aufrufen. file_name endet auf .pdf und ist einschließlich DB-Escaping höchstens 150 Zeichen; tatsächliche Datei wird erst bei verify geprüft.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche attachment_/pdftemplate_-ID, insert=true und signierte Upload-URL; noch keine verifizierte Datei.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attachment_012345abcdef_123456","insert":true,"upload_url":"https://example.org/signed-upload"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"insert":{"type":"boolean"},"upload_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["id","insert","upload_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"enum":["attachment","pdftemplate"]},"file_name":{"type":"string","maxLength":150,"pattern":".*\\.[pP][dD][fF]$"},"file_type":{"const":"application/pdf"},"file_size":{"type":["integer","string"],"description":"Bytes, numerisch nach int konvertiert. Strikt größer als 20; bei attachment strikt kleiner als 1500000, bei pdftemplate strikt kleiner als 1000000."}},"additionalProperties":true,"required":["emailtemplate","type","file_name","file_type","file_size"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"PDF-Datei an einer Vorlage zum Upload reservieren","value":{"data":{"emailtemplate":"et_012345abcdef_123456","type":"attachment","file_name":"Information.pdf","file_type":"application/pdf","file_size":1200}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Emailtemplatefunc.php","line":183,"reason":"segment3 ignored; data.type validates attachment/pdftemplate; Vue sends same literal in URL.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","method":"_addObject"},"x-edoobox-upload-ticket":{"provider":"s3","bucketEnv":"AWS_S3_BUCKET","regionEnv":"AWS_S3_REGION","urlPointer":"/data/upload_url","pathPrefixTemplate":"/ED{fid}F_{appVersion}/mail/","method":"PUT","minBytes":21,"mimeType":"application/pdf","expiresInSeconds":1800,"source":{"file":"application/libraries/edoobox/integrations/file/File.php","line":296},"maxBytes":1499999,"workflowSelector":{"requestPointer":"/body/data/type","variants":{"attachment":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_attachment","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]},"pdftemplate":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_pdf_template","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}}},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_attachment","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-upload-input-review":{"rawFilenameMaxLength":150,"escapedFilenameMaxLength":150,"source":{"file":"application/models/v2/Emailtemplatefunc.php","line":291,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","lineEnd":296},"extensionSource":{"file":"application/models/v2/Emailtemplatefunc.php","line":13,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","method":"isValidPDFFileName","lineEnd":18}},"x-edoobox-upload-kind-review":{"requestPointer":"/body/data/type","nativeEffectiveByteLimits":{"attachment":1499999,"pdftemplate":999999},"minimumBytes":21,"ticketCapMeaning":"Union cap; the request schema enforces the per-kind maximum before native metadata creation and the staged transfer must exactly equal that validated file_size. Runtime must choose the reviewed workflow variant from the unchanged validated request, with no URL/default fallback.","sources":[{"file":"application/models/v2/Emailtemplate.php","line":184,"lineEnd":193,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","reason":"Any third segment reaching add_object delegates to the same native handler; list is an actual alias."},{"file":"application/models/v2/Emailtemplatefunc.php","line":9,"lineEnd":26,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Native size predicate requires >20 and excludes1500000 for attachments and1000000 for templates."},{"file":"application/models/v2/Emailtemplatefunc.php","line":183,"lineEnd":210,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Payload type selects attachment/pdftemplate independently of contextual URL spelling; actual parent ownership remains native."},{"file":"application/models/v2/Emailtemplatefunc.php","line":298,"lineEnd":357,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Native integer-cast size validation and selected per-body-kind insert/public child response; valid cross-type URL payloads remain valid."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"postEmailtemplate","line":184,"branch":{"file":"application/models/v2/Emailtemplate.php","line":190}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/list E-Mail- und PDF-Vorlagen auflisten Liest gespeicherte Sprachfassungen von E-Mail- und PDF-Vorlagen. id bezeichnet die einzelne Sprachfassung (et_), template die Vorlagenfamilie (emailtemplate_). type und compatibility sind unveränderliche Familienmerkmale. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_emailtemplate_list - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-list-849e32ef406d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_list","summary":"E-Mail- und PDF-Vorlagen auflisten","description":"Liest gespeicherte Sprachfassungen von E-Mail- und PDF-Vorlagen. id bezeichnet die einzelne Sprachfassung (et_), template die Vorlagenfamilie (emailtemplate_). type und compatibility sind unveränderliche Familienmerkmale. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"et_012345abcdef_123456":{"id":"et_012345abcdef_123456","template":"emailtemplate_012345abcdef_123456","type":"email","compatibility":null,"language":"de","template_name":"Kursinformation","subject":"Information zu Ihrem Kurs","html":"

Herzlich willkommen.

","status":true,"template_account":true,"isLUT":false,"pdf":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["emailtemplate_012345abcdef_123456"]},"template":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":197,"reason":"POST recognizes the literal new before selecting/creating the account template.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":39,"reason":"Normal template input decodes an account/global public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplate.php","line":44,"reason":"Response encodes the corresponding public emailtemplate ID.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"}]},"type":{"type":"string","enum":["email","pdf"],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":57,"lineStart":57,"lineEnd":74,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"367f64084ab5206612e18044de58df94cda49035c3c3d923d71e0281acc31e39","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"compatibility":{"anyOf":[{"type":"string"},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":75,"lineStart":75,"lineEnd":86,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8f0d83c5c7d1752135b97553ec1110d5e42856ccea20b71b5d66c6f8fe3c0427","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Create dispatch, metadata preparation and existing language/family guard."},{"file":"application/models/v2/Emailtemplate.php","line":490,"lineStart":490,"lineEnd":552,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"d4d6aa9bfcf7c3b4b1c276bbf7ad570cfff3fe8dec852052f6f626020ce8903d","reason":"Actual metadata constraints, inheritance and computed values."},{"file":"application/models/v2/Emailtemplate.php","line":600,"lineStart":600,"lineEnd":650,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"2514385863709f3435d4f3852887b6b9d8955715ae36fb3abd185be3c8d1afe1","reason":"Family lookup, v2 ID decoding and normalizers/error payload."},{"file":"application/libraries/edoobox/integrations/template/EdTemplate.php","line":8,"lineStart":8,"lineEnd":12,"sha256":"4f9dd1798fb2afed053158dbdf0fe19e6914dd9e6d641347be90a396c9f63f42","spanSha256":"34fd884188a6556b237211cbcf1dddd4a296ae812131462b6ddd6be7244062c8","reason":"Exact email/pdf/participant_document constants."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"language":{"anyOf":[{"type":"string","enum":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"]},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":87,"lineStart":87,"lineEnd":100,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9459c79f5d324a06eeffe983f8f13a7f20f90a8234da77942235d89d0093877e","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":566,"lineStart":566,"lineEnd":579,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"532f901c7010f0ee0967615986887785dd4d9c3cfbfe1a23a256f9de6e5efcc8","reason":"Output key lookup."},{"file":"application/helpers/langedoobox_helper.php","line":718,"lineStart":718,"lineEnd":727,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"193c944f9e8fc80de14db73e0742b2517edf9d0523abf4f1fc6386da4d6e453d","reason":"Input fallback to language1."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys and de=1."},{"file":"application/models/v2/Emailtemplate.php","line":231,"lineStart":231,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"01a86f24c62d7b2a42bda361b518424d71970eac566c08ad8458b7fa87635dca","reason":"Family/language duplicate guard."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"template_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"type":"string"},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":123,"reason":"Generic create uses the html text getter.","sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01"},{"file":"application/models/v2/Emailtemplatefunc.php","line":34,"reason":"Update callback consumes raw html text, may rewrite PDF asset URLs, then calls aP with length 65500.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":141,"type":"boolean"},"template_account":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":150,"lineStart":150,"lineEnd":160,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"9edc2de5e8f00231a327f1af03efc3a54b9e783b92605268fdf1ce5224eb8234","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":195,"lineStart":195,"lineEnd":258,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"8ca392c096320f7ed5277beb9da92c1460b1c13006991fd13b15fa8587f6f287","reason":"Family selection/metadata preparation occurs before generic field iteration."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"isLUT":{"const":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Emailtemplate.php","line":161,"lineStart":161,"lineEnd":170,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"46bce62713ddabf93dde5f2e642571c2abf2f383794e373cb50419b9054b4b5e","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Emailtemplate.php","line":184,"lineStart":184,"lineEnd":267,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","spanSha256":"b1f93786a236f329dec63ac50fd6b97f35e6626b17a4d9ed3e7ee478a176ab20","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/emailtemplate/list/add_object PDF-Datei an einer Vorlage zum Upload reservieren Reserviert einen PDF-Anhang (type=attachment) oder PDF-Hintergrund (type=pdftemplate) und liefert eine signierte HTTPS-Upload-URL. Der Typ wird aus data.type gelesen, unabhängig vom Segment vor add_object. emailtemplate ist die et_-ID einer vorhandenen Kontovorlage. Anhänge sind nur an E-Mail-Vorlagen erlaubt, höchstens fünf einschließlich offener Reservierungen; PDF-Hintergründe nur an PDF-Vorlagen und höchstens einer. Subscription muss Vorlagen erlauben. Datei anschließend an upload_url hochladen und verify_object aufrufen. file_name endet auf .pdf und ist einschließlich DB-Escaping höchstens 150 Zeichen; tatsächliche Datei wird erst bei verify geprüft. - Operation-ID: post_v2_emailtemplate_list_add_object - Dokumentation: /docs/api/v2/operations/post-v2-emailtemplate-list-add-object-cd136be0ddc5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "emailtemplate": "et_012345abcdef_123456", "type": "attachment", "file_name": "Information.pdf", "file_type": "application/pdf", "file_size": 1200 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/list/add_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/list/add_object"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "emailtemplate": "et_012345abcdef_123456", "type": "attachment", "file_name": "Information.pdf", "file_type": "application/pdf", "file_size": 1200 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/list/add_object" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"emailtemplate\":\"et_012345abcdef_123456\",\"type\":\"attachment\",\"file_name\":\"Information.pdf\",\"file_type\":\"application/pdf\",\"file_size\":1200}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_emailtemplate_list_add_object","summary":"PDF-Datei an einer Vorlage zum Upload reservieren","description":"Reserviert einen PDF-Anhang (type=attachment) oder PDF-Hintergrund (type=pdftemplate) und liefert eine signierte HTTPS-Upload-URL. Der Typ wird aus data.type gelesen, unabhängig vom Segment vor add_object. emailtemplate ist die et_-ID einer vorhandenen Kontovorlage. Anhänge sind nur an E-Mail-Vorlagen erlaubt, höchstens fünf einschließlich offener Reservierungen; PDF-Hintergründe nur an PDF-Vorlagen und höchstens einer. Subscription muss Vorlagen erlauben. Datei anschließend an upload_url hochladen und verify_object aufrufen. file_name endet auf .pdf und ist einschließlich DB-Escaping höchstens 150 Zeichen; tatsächliche Datei wird erst bei verify geprüft.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche attachment_/pdftemplate_-ID, insert=true und signierte Upload-URL; noch keine verifizierte Datei.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attachment_012345abcdef_123456","insert":true,"upload_url":"https://example.org/signed-upload"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"insert":{"type":"boolean"},"upload_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["id","insert","upload_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"enum":["attachment","pdftemplate"]},"file_name":{"type":"string","maxLength":150,"pattern":".*\\.[pP][dD][fF]$"},"file_type":{"const":"application/pdf"},"file_size":{"type":["integer","string"],"description":"Bytes, numerisch nach int konvertiert. Strikt größer als 20; bei attachment strikt kleiner als 1500000, bei pdftemplate strikt kleiner als 1000000."}},"additionalProperties":true,"required":["emailtemplate","type","file_name","file_type","file_size"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"PDF-Datei an einer Vorlage zum Upload reservieren","value":{"data":{"emailtemplate":"et_012345abcdef_123456","type":"attachment","file_name":"Information.pdf","file_type":"application/pdf","file_size":1200}}}}}}},"x-edoobox-upload-input-review":{"rawFilenameMaxLength":150,"escapedFilenameMaxLength":150,"source":{"file":"application/models/v2/Emailtemplatefunc.php","line":291,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","lineEnd":296},"extensionSource":{"file":"application/models/v2/Emailtemplatefunc.php","line":13,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","method":"isValidPDFFileName","lineEnd":18}},"x-edoobox-upload-ticket":{"provider":"s3","bucketEnv":"AWS_S3_BUCKET","regionEnv":"AWS_S3_REGION","urlPointer":"/data/upload_url","pathPrefixTemplate":"/ED{fid}F_{appVersion}/mail/","method":"PUT","minBytes":21,"mimeType":"application/pdf","expiresInSeconds":1800,"source":{"file":"application/libraries/edoobox/integrations/file/File.php","line":296},"maxBytes":1499999,"workflowSelector":{"requestPointer":"/body/data/type","variants":{"attachment":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_attachment","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]},"pdftemplate":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_pdf_template","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}}},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_attachment","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-upload-kind-review":{"requestPointer":"/body/data/type","nativeEffectiveByteLimits":{"attachment":1499999,"pdftemplate":999999},"minimumBytes":21,"ticketCapMeaning":"Union cap; the request schema enforces the per-kind maximum before native metadata creation and the staged transfer must exactly equal that validated file_size. Runtime must choose the reviewed workflow variant from the unchanged validated request, with no URL/default fallback.","sources":[{"file":"application/models/v2/Emailtemplate.php","line":184,"lineEnd":193,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","reason":"Any third segment reaching add_object delegates to the same native handler; list is an actual alias."},{"file":"application/models/v2/Emailtemplatefunc.php","line":9,"lineEnd":26,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Native size predicate requires >20 and excludes1500000 for attachments and1000000 for templates."},{"file":"application/models/v2/Emailtemplatefunc.php","line":183,"lineEnd":210,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Payload type selects attachment/pdftemplate independently of contextual URL spelling; actual parent ownership remains native."},{"file":"application/models/v2/Emailtemplatefunc.php","line":298,"lineEnd":357,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Native integer-cast size validation and selected per-body-kind insert/public child response; valid cross-type URL payloads remain valid."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"postEmailtemplate","line":184,"branch":{"file":"application/models/v2/Emailtemplate.php","line":190}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/list/attachment PDF-Anhänge auflisten Liest die PDF-Anhänge von E-Mail-Vorlagen im ausgewählten Konto. filter auf emailtemplate verwendet die et_-ID der Sprachfassung. verify=false kennzeichnet eine noch nicht bestätigte Upload-Reservierung. - Operation-ID: get_v2_emailtemplate_list_attachment - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-list-attachment-85d5815fda36/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/list/attachment' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/list/attachment"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/list/attachment" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_list_attachment","summary":"PDF-Anhänge auflisten","description":"Liest die PDF-Anhänge von E-Mail-Vorlagen im ausgewählten Konto. filter auf emailtemplate verwendet die et_-ID der Sprachfassung. verify=false kennzeichnet eine noch nicht bestätigte Upload-Reservierung.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Dateisammlung mit öffentlicher attachment_/pdftemplate_-ID, Eltern-Sprachfassung, Name, Größe, Verifizierungsstatus und Änderungszeit.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"attachment_012345abcdef_123456":{"id":"attachment_012345abcdef_123456","emailtemplate":"et_012345abcdef_123456","file_name":"Information.pdf","file_size":1200,"verify":true,"last_change":"2026-10-22T10:00:00+00:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer"},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer"},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/9","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":358,"reason":"APIPermissions resource mapping to emtp.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Lists existing attachment metadata associated with tenant-owned templates; it does not request upload URLs or verify uploaded objects.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/9","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":342}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/list/LUT Aktive Systemvorlagen als Katalog lesen Liefert aktive Systemvorlagen des globalen Vorlagenkatalogs anstelle der kontoeigenen Sprachfassungen. isLUT=true kennzeichnet Katalogeinträge; pdf/type unterscheiden E-Mail- und PDF-Vorlagen. template_account wird auch bei eingeschränkter fields-Auswahl ergänzt. - Operation-ID: get_v2_emailtemplate_list_LUT - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-list-lut-b4d946924cd5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/list/LUT' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/list/LUT"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/list/LUT" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_list_LUT","summary":"Aktive Systemvorlagen als Katalog lesen","description":"Liefert aktive Systemvorlagen des globalen Vorlagenkatalogs anstelle der kontoeigenen Sprachfassungen. isLUT=true kennzeichnet Katalogeinträge; pdf/type unterscheiden E-Mail- und PDF-Vorlagen. template_account wird auch bei eingeschränkter fields-Auswahl ergänzt.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Vorlagensammlung bzw. Liste bei response_type=array; Einträge besitzen isLUT=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"et_012345abcdef_123456":{"id":"et_012345abcdef_123456","template":"emailtemplate_012345abcdef_123456","type":"email","compatibility":null,"language":"de","template_name":"Kursinformation","subject":"Information zu Ihrem Kurs","html":"

Herzlich willkommen.

","status":true,"template_account":false,"isLUT":true,"pdf":false}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/7","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":358,"reason":"APIPermissions resource mapping to emtp.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Lists active global system templates through an explicit global-owner SELECT override; only presentation metadata is added.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/7","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":412}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/list/pdf_template PDF-Hintergründe auflisten Liest die PDF-Hintergrunddateien von PDF-Vorlagen im ausgewählten Konto. filter auf emailtemplate verwendet die et_-ID der Sprachfassung. verify=false kennzeichnet eine noch nicht bestätigte Upload-Reservierung. - Operation-ID: get_v2_emailtemplate_list_pdf_template - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-list-pdf-template-2247f9e3d5ff/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/list/pdf_template' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/list/pdf_template"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/list/pdf_template" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_list_pdf_template","summary":"PDF-Hintergründe auflisten","description":"Liest die PDF-Hintergrunddateien von PDF-Vorlagen im ausgewählten Konto. filter auf emailtemplate verwendet die et_-ID der Sprachfassung. verify=false kennzeichnet eine noch nicht bestätigte Upload-Reservierung.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"emailtemplate_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Dateisammlung mit öffentlicher attachment_/pdftemplate_-ID, Eltern-Sprachfassung, Name, Größe, Verifizierungsstatus und Änderungszeit.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"pdftemplate_012345abcdef_123456":{"id":"pdftemplate_012345abcdef_123456","emailtemplate":"et_012345abcdef_123456","file_name":"Information.pdf","file_size":1200,"verify":true,"last_change":"2026-10-22T10:00:00+00:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer"},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer"},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/10","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":358,"reason":"APIPermissions resource mapping to emtp.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Lists existing PDF template attachment metadata with tenant ownership inherited through its parent template.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/10","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":350}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/list/pdf_template_url Signierte Lese-URL einer Vorlagendatei abrufen Liefert Metadaten und eine signierte Lese-URL für einen bereits verifizierten Anhang oder PDF-Hintergrund des Kontos. Die ID bezeichnet attachment_ oder pdftemplate_. Die Aktion lädt keine Datei hoch und ändert keine Vorlagen. Ein filter-Eintrag property=id ist Pflicht. - Operation-ID: get_v2_emailtemplate_list_pdf_template_url - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-list-pdf-template-url-3266df3e35f8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | filter | query | ja | JSON-Filter mit öffentlicher pdftemplate_-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"filter","in":"query","required":true,"description":"JSON-Filter mit öffentlicher pdftemplate_-ID.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"pdftemplate_012345abcdef_123456"}]}}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/list/pdf_template_url' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/list/pdf_template_url"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/list/pdf_template_url" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_list_pdf_template_url","summary":"Signierte Lese-URL einer Vorlagendatei abrufen","description":"Liefert Metadaten und eine signierte Lese-URL für einen bereits verifizierten Anhang oder PDF-Hintergrund des Kontos. Die ID bezeichnet attachment_ oder pdftemplate_. Die Aktion lädt keine Datei hoch und ändert keine Vorlagen. Ein filter-Eintrag property=id ist Pflicht.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"filter","in":"query","required":true,"description":"JSON-Filter mit öffentlicher pdftemplate_-ID.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"pdftemplate_012345abcdef_123456"}]}}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nach öffentlicher Datei-ID indiziertes Objekt; das URL-Feld heißt für beide Dateitypen attachment_url.","content":{"application/json":{"schema":{"type":"object","required":["data","error"],"properties":{"data":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer"},"last_change":{"type":"string","format":"date-time"},"attachment_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["id","emailtemplate","file_name","file_size","last_change","attachment_url"]}},"error":{"const":false},"type":{"type":"string"},"success":{"const":true}},"additionalProperties":true},"example":{"data":{"attachment_012345abcdef_123456":{"id":"attachment_012345abcdef_123456","emailtemplate":"et_012345abcdef_123456","file_name":"Information.pdf","file_size":1200,"last_change":"2026-10-22T10:00:00+00:00","attachment_url":"https://example.org/signed-download"}},"error":false,"type":"admin","success":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Authfunc.php","method":"RequestAPIData","line":2191},"x-edoobox-query-selector":{"parameter":"filter","property":"id","idType":"pdftemplate","requiresVerifiedOwnedChild":true,"nativeExtraFilterProperties":"ignored","source":{"file":"application/models/v2/Emailtemplatefunc.php","method":"_showPDFAttachment"}},"x-edoobox-user-handoff":{"sensitivePaths":["/data"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/*/attachment_url"],"x-edoobox-user-handoff-source":{"file":"application/models/v2/Emailtemplatefunc.php","method":"_showPDFAttachment","line":554,"lineEnd":642,"reason":"The typed child lookup is constrained to an account-owned parent, rejects pending objects, and returns a signed storage URL alongside child/parent v2 IDs and file metadata. Deliver the complete data map only through the protected one-time authenticated human handoff. Signed URL, signature and legacy object path must not enter model results, previews, logs or normal result caches. Upstream URL lifetime is unchanged.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f"},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":345}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/emailtemplate/list/PdfDefaultNames_LUT Standard-Dateinamen für PDF- und Kalenderanhänge lesen Liefert je PDF-Vorlagenfamilie und für ics den bereinigten Standard-Dateinamen in den aktiven Kontosprachen. Die Dateiendung .pdf bzw. .ics ist im Namen entfernt. Die Werte können für die Dateinamenkonfiguration verwendet werden. - Operation-ID: get_v2_emailtemplate_list_PdfDefaultNames_LUT - Dokumentation: /docs/api/v2/operations/get-v2-emailtemplate-list-pdfdefaultnames-lut-44de7e0a56f7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/list/PdfDefaultNames_LUT' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/list/PdfDefaultNames_LUT"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/list/PdfDefaultNames_LUT" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_emailtemplate_list_PdfDefaultNames_LUT","summary":"Standard-Dateinamen für PDF- und Kalenderanhänge lesen","description":"Liefert je PDF-Vorlagenfamilie und für ics den bereinigten Standard-Dateinamen in den aktiven Kontosprachen. Die Dateiendung .pdf bzw. .ics ist im Namen entfernt. Die Werte können für die Dateinamenkonfiguration verwendet werden.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Nach emailtemplate_-ID bzw. ics indiziertes Objekt mit sprachabhängigen Dateinamen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"emailtemplate_012345abcdef_123456":{"id":"emailtemplate_012345abcdef_123456","lang":{"de":"Buchungsbestaetigung"}},"ics":{"id":"ics","lang":{"de":"Kalendereintrag"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"const":"ics"}]},"lang":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true,"required":["id","lang"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/8","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":358,"reason":"APIPermissions resource mapping to emtp.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Derives translated default PDF/ICS filenames from static template families and the current account languages.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/8","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"getEmailtemplate","line":330,"branch":{"file":"application/models/v2/Emailtemplate.php","line":368}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/emailtemplate/pdftemplate/add_object PDF-Datei an einer Vorlage zum Upload reservieren Reserviert einen PDF-Anhang (type=attachment) oder PDF-Hintergrund (type=pdftemplate) und liefert eine signierte HTTPS-Upload-URL. Der Typ wird aus data.type gelesen, unabhängig vom Segment vor add_object. emailtemplate ist die et_-ID einer vorhandenen Kontovorlage. Anhänge sind nur an E-Mail-Vorlagen erlaubt, höchstens fünf einschließlich offener Reservierungen; PDF-Hintergründe nur an PDF-Vorlagen und höchstens einer. Subscription muss Vorlagen erlauben. Datei anschließend an upload_url hochladen und verify_object aufrufen. file_name endet auf .pdf und ist einschließlich DB-Escaping höchstens 150 Zeichen; tatsächliche Datei wird erst bei verify geprüft. - Operation-ID: post_v2_emailtemplate_pdftemplate_add_object - Dokumentation: /docs/api/v2/operations/post-v2-emailtemplate-pdftemplate-add-object-7c22f7d7ddde/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "emailtemplate": "et_012345abcdef_123456", "type": "attachment", "file_name": "Information.pdf", "file_type": "application/pdf", "file_size": 1200 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/emailtemplate/pdftemplate/add_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/emailtemplate/pdftemplate/add_object"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "emailtemplate": "et_012345abcdef_123456", "type": "attachment", "file_name": "Information.pdf", "file_type": "application/pdf", "file_size": 1200 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/emailtemplate/pdftemplate/add_object" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"emailtemplate\":\"et_012345abcdef_123456\",\"type\":\"attachment\",\"file_name\":\"Information.pdf\",\"file_type\":\"application/pdf\",\"file_size\":1200}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_emailtemplate_pdftemplate_add_object","summary":"PDF-Datei an einer Vorlage zum Upload reservieren","description":"Reserviert einen PDF-Anhang (type=attachment) oder PDF-Hintergrund (type=pdftemplate) und liefert eine signierte HTTPS-Upload-URL. Der Typ wird aus data.type gelesen, unabhängig vom Segment vor add_object. emailtemplate ist die et_-ID einer vorhandenen Kontovorlage. Anhänge sind nur an E-Mail-Vorlagen erlaubt, höchstens fünf einschließlich offener Reservierungen; PDF-Hintergründe nur an PDF-Vorlagen und höchstens einer. Subscription muss Vorlagen erlauben. Datei anschließend an upload_url hochladen und verify_object aufrufen. file_name endet auf .pdf und ist einschließlich DB-Escaping höchstens 150 Zeichen; tatsächliche Datei wird erst bei verify geprüft.","tags":["emailtemplate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche attachment_/pdftemplate_-ID, insert=true und signierte Upload-URL; noch keine verifizierte Datei.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"attachment_012345abcdef_123456","insert":true,"upload_url":"https://example.org/signed-upload"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^attachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^pdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"insert":{"type":"boolean"},"upload_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["id","insert","upload_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"emailtemplate":{"type":"string","pattern":"^et_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"type":{"enum":["attachment","pdftemplate"]},"file_name":{"type":"string","maxLength":150,"pattern":".*\\.[pP][dD][fF]$"},"file_type":{"const":"application/pdf"},"file_size":{"type":["integer","string"],"description":"Bytes, numerisch nach int konvertiert. Strikt größer als 20; bei attachment strikt kleiner als 1500000, bei pdftemplate strikt kleiner als 1000000."}},"additionalProperties":true,"required":["emailtemplate","type","file_name","file_type","file_size"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"PDF-Datei an einer Vorlage zum Upload reservieren","value":{"data":{"emailtemplate":"et_012345abcdef_123456","type":"attachment","file_name":"Information.pdf","file_type":"application/pdf","file_size":1200}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Emailtemplatefunc.php","line":183,"reason":"segment3 ignored; data.type validates attachment/pdftemplate; Vue sends same literal in URL.","sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","method":"_addObject"},"x-edoobox-upload-ticket":{"provider":"s3","bucketEnv":"AWS_S3_BUCKET","regionEnv":"AWS_S3_REGION","urlPointer":"/data/upload_url","pathPrefixTemplate":"/ED{fid}F_{appVersion}/mail/","method":"PUT","minBytes":21,"mimeType":"application/pdf","expiresInSeconds":1800,"source":{"file":"application/libraries/edoobox/integrations/file/File.php","line":296},"maxBytes":1499999,"workflowSelector":{"requestPointer":"/body/data/type","variants":{"attachment":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_attachment","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]},"pdftemplate":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_pdf_template","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}}},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_emailtemplate_id_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_emailtemplate_list_pdf_template","argumentBindings":[{"target":"/query/filter/0/value","source":"/data/id"}],"argumentTemplate":{"query":{"filter":[{"property":"id","expression":"="}]}},"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_emailtemplate_id_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned child upload ID, never the parent emailtemplate ID.","Inspect the native error envelope and verification result. A successful storage PUT alone does not finalize the upload.","Abort is a separately reviewed mutation; storage deletion can fail and must not be represented as guaranteed cleanup.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-upload-input-review":{"rawFilenameMaxLength":150,"escapedFilenameMaxLength":150,"source":{"file":"application/models/v2/Emailtemplatefunc.php","line":291,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","lineEnd":296},"extensionSource":{"file":"application/models/v2/Emailtemplatefunc.php","line":13,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","method":"isValidPDFFileName","lineEnd":18}},"x-edoobox-upload-kind-review":{"requestPointer":"/body/data/type","nativeEffectiveByteLimits":{"attachment":1499999,"pdftemplate":999999},"minimumBytes":21,"ticketCapMeaning":"Union cap; the request schema enforces the per-kind maximum before native metadata creation and the staged transfer must exactly equal that validated file_size. Runtime must choose the reviewed workflow variant from the unchanged validated request, with no URL/default fallback.","sources":[{"file":"application/models/v2/Emailtemplate.php","line":184,"lineEnd":193,"sha256":"b64dc4fb02c53f520ed472daec7866ba4de3d9c7632f19011ef63bd726721a01","reason":"Any third segment reaching add_object delegates to the same native handler; list is an actual alias."},{"file":"application/models/v2/Emailtemplatefunc.php","line":9,"lineEnd":26,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Native size predicate requires >20 and excludes1500000 for attachments and1000000 for templates."},{"file":"application/models/v2/Emailtemplatefunc.php","line":183,"lineEnd":210,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Payload type selects attachment/pdftemplate independently of contextual URL spelling; actual parent ownership remains native."},{"file":"application/models/v2/Emailtemplatefunc.php","line":298,"lineEnd":357,"sha256":"c25b711ef69b6cf6bb0bd4946a4f2ff0c2467fc1a8cc7a2df48187ddcb9bd60f","reason":"Native integer-cast size validation and selected per-body-kind insert/public child response; valid cross-type URL payloads remain valid."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Emailtemplate.php","method":"postEmailtemplate","line":184,"branch":{"file":"application/models/v2/Emailtemplate.php","line":190}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"emailtemplate","short":"emtp","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/exportmanager Individuelle Exportdefinition löschen Markiert die Definition des Kontos als gelöscht und deaktiviert sie. Ohne Pfad-ID ist id im Query oder JSON-Body erforderlich. - Operation-ID: delete_v2_exportmanager - Dokumentation: /docs/api/v2/operations/delete-v2-exportmanager-3e9c96d7ffd8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Erforderlich im Query oder top-level im JSON-Body. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Erforderlich im Query oder top-level im JSON-Body.","example":"exportmanager_012345abcdef_123456"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "exportmanager_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/exportmanager' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "exportmanager_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"exportmanager_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_exportmanager","summary":"Individuelle Exportdefinition löschen","description":"Markiert die Definition des Kontos als gelöscht und deaktiviert sie. Ohne Pfad-ID ist id im Query oder JSON-Body erforderlich.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Erforderlich im Query oder top-level im JSON-Body.","example":"exportmanager_012345abcdef_123456"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"exportmanager_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"Individuelle Exportdefinition löschen","value":{"id":"exportmanager_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"deleteExportmanager","line":234},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/exportmanager Gespeicherte individuelle Exportdefinitionen lesen Liest eine Definition per Pfad-ID bzw. id-Filter oder alle nicht gelöschten Definitionen nach Name sortiert. available_in_offer filtert die Angebotsverfügbarkeit; true beschränkt zusätzlich auf aktive Definitionen. Andere generische Filter, Feldauswahl, Sortierung und Pagination werden ignoriert. - Operation-ID: get_v2_exportmanager - Dokumentation: /docs/api/v2/operations/get-v2-exportmanager-e936c59e176b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | run | query | nein | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","available_in_offer"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"available_in_offer","value":true}]}}},{"name":"run","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/exportmanager' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_exportmanager","summary":"Gespeicherte individuelle Exportdefinitionen lesen","description":"Liest eine Definition per Pfad-ID bzw. id-Filter oder alle nicht gelöschten Definitionen nach Name sortiert. available_in_offer filtert die Angebotsverfügbarkeit; true beschränkt zusätzlich auf aktive Definitionen. Andere generische Filter, Feldauswahl, Sortierung und Pagination werden ignoriert.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","available_in_offer"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"available_in_offer","value":true}]}}},{"name":"run","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Ein Objekt beim id-Selektor, sonst Array mit tatsächlichen limit-Listenzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"exportmanager_012345abcdef_123456","name":"Kontobezeichnung","description":"Beispieldefinition","enabled":true,"available_in_offer":false,"definition":{"name":"Kontobezeichnung","description":"Beispieldefinition","contexts":["global"],"grain":"account","inputs":[],"filters":[],"columns":[{"field":"account.name","key":"account_name","label":"Organisation"}],"enabled":true,"available_in_offer":false},"definition_version":1,"created_at":1792627200,"updated_at":1792627200}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Exportmanager.php","line":61},"reason":"Read definitions, catalogs, existing run status or completed output; no job processing in these GET branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"getExportmanager","line":61},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/exportmanager Individuelle Exportdefinition anlegen Speichert eine vollständige validierte Definition. name und mindestens eine gültige columns-Spalte sind erforderlich; Inputs, Filter und grain müssen zu Katalog und Datenabhängigkeiten passen. Metadaten können in data oder data.definition stehen; äußere Metadaten überschreiben innere. contexts und available_in_offer werden neu abgeleitet. Noch kein CSV-Lauf wird erzeugt. - Operation-ID: post_v2_exportmanager - Dokumentation: /docs/api/v2/operations/post-v2-exportmanager-fb43893961c0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/exportmanager' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Angebotsliste\",\"grain\":\"offer\",\"columns\":[{\"field\":\"offer.id\",\"label\":\"ID\"},{\"field\":\"offer.name\",\"label\":\"Angebot\"}],\"inputs\":[{\"field\":\"offer.id\",\"mode\":\"runtime\"}],\"filters\":[],\"enabled\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_exportmanager","summary":"Individuelle Exportdefinition anlegen","description":"Speichert eine vollständige validierte Definition. name und mindestens eine gültige columns-Spalte sind erforderlich; Inputs, Filter und grain müssen zu Katalog und Datenabhängigkeiten passen. Metadaten können in data oder data.definition stehen; äußere Metadaten überschreiben innere. contexts und available_in_offer werden neu abgeleitet. Noch kein CSV-Lauf wird erzeugt.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"exportmanager_012345abcdef_123456","export":{"id":"exportmanager_012345abcdef_123456","name":"Kontobezeichnung","description":"Beispieldefinition","enabled":true,"available_in_offer":false,"definition":{"name":"Kontobezeichnung","description":"Beispieldefinition","contexts":["global"],"grain":"account","inputs":[],"filters":[],"columns":[{"field":"account.name","key":"account_name","label":"Organisation"}],"enabled":true,"available_in_offer":false},"definition_version":1,"created_at":1792627200,"updated_at":1792627200}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"export":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100,"minItems":1},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte.","required":["name","columns"]},{"type":"object","properties":{"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"required":["definition"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Individuelle Exportdefinition anlegen","value":{"data":{"name":"Angebotsliste","grain":"offer","columns":[{"field":"offer.id","label":"ID"},{"field":"offer.name","label":"Angebot"}],"inputs":[{"field":"offer.id","mode":"runtime"}],"filters":[],"enabled":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"postExportmanager","line":117},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/exportmanager Individuelle Exportdefinition ersetzen Speichert eine vollständige validierte Definition. name und mindestens eine gültige columns-Spalte sind erforderlich; Inputs, Filter und grain müssen zu Katalog und Datenabhängigkeiten passen. Metadaten können in data oder data.definition stehen; äußere Metadaten überschreiben innere. contexts und available_in_offer werden neu abgeleitet. PUT ersetzt vollständig und ist kein partielles Patch. - Operation-ID: put_v2_exportmanager - Dokumentation: /docs/api/v2/operations/put-v2-exportmanager-ff69d4b83871/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true }, "id": "exportmanager_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/exportmanager' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true }, "id": "exportmanager_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Angebotsliste\",\"grain\":\"offer\",\"columns\":[{\"field\":\"offer.id\",\"label\":\"ID\"},{\"field\":\"offer.name\",\"label\":\"Angebot\"}],\"inputs\":[{\"field\":\"offer.id\",\"mode\":\"runtime\"}],\"filters\":[],\"enabled\":true},\"id\":\"exportmanager_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_exportmanager","summary":"Individuelle Exportdefinition ersetzen","description":"Speichert eine vollständige validierte Definition. name und mindestens eine gültige columns-Spalte sind erforderlich; Inputs, Filter und grain müssen zu Katalog und Datenabhängigkeiten passen. Metadaten können in data oder data.definition stehen; äußere Metadaten überschreiben innere. contexts und available_in_offer werden neu abgeleitet. PUT ersetzt vollständig und ist kein partielles Patch.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"exportmanager_012345abcdef_123456","export":{"id":"exportmanager_012345abcdef_123456","name":"Kontobezeichnung","description":"Beispieldefinition","enabled":true,"available_in_offer":false,"definition":{"name":"Kontobezeichnung","description":"Beispieldefinition","contexts":["global"],"grain":"account","inputs":[],"filters":[],"columns":[{"field":"account.name","key":"account_name","label":"Organisation"}],"enabled":true,"available_in_offer":false},"definition_version":1,"created_at":1792627200,"updated_at":1792627200}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"export":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100,"minItems":1},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte.","required":["name","columns"]},{"type":"object","properties":{"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"required":["definition"]}]},"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Individuelle Exportdefinition ersetzen","value":{"data":{"name":"Angebotsliste","grain":"offer","columns":[{"field":"offer.id","label":"ID"},{"field":"offer.name","label":"Angebot"}],"inputs":[{"field":"offer.id","mode":"runtime"}],"filters":[],"enabled":true},"id":"exportmanager_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"updateExportmanager","line":183},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/exportmanager/{id} Individuelle Exportdefinition löschen Markiert die Definition des Kontos als gelöscht und deaktiviert sie. - Operation-ID: delete_v2_exportmanager_id - Dokumentation: /docs/api/v2/operations/delete-v2-exportmanager-id-ca2b0fd5aaa1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche exportmanager-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche exportmanager-ID.","schema":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_exportmanager_id","summary":"Individuelle Exportdefinition löschen","description":"Markiert die Definition des Kontos als gelöscht und deaktiviert sie.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche exportmanager-ID.","schema":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"exportmanager_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"Individuelle Exportdefinition löschen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"deleteExportmanager","line":234},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/exportmanager/{id} Gespeicherte individuelle Exportdefinitionen lesen Liest eine Definition per Pfad-ID bzw. id-Filter oder alle nicht gelöschten Definitionen nach Name sortiert. available_in_offer filtert die Angebotsverfügbarkeit; true beschränkt zusätzlich auf aktive Definitionen. Andere generische Filter, Feldauswahl, Sortierung und Pagination werden ignoriert. - Operation-ID: get_v2_exportmanager_id - Dokumentation: /docs/api/v2/operations/get-v2-exportmanager-id-4ee9aa6b387f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | run | query | nein | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["exportmanager_012345abcdef_123456"]}},{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","available_in_offer"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"available_in_offer","value":true}]}}},{"name":"run","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_exportmanager_id","summary":"Gespeicherte individuelle Exportdefinitionen lesen","description":"Liest eine Definition per Pfad-ID bzw. id-Filter oder alle nicht gelöschten Definitionen nach Name sortiert. available_in_offer filtert die Angebotsverfügbarkeit; true beschränkt zusätzlich auf aktive Definitionen. Andere generische Filter, Feldauswahl, Sortierung und Pagination werden ignoriert.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["exportmanager_012345abcdef_123456"]}},{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","available_in_offer"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"available_in_offer","value":true}]}}},{"name":"run","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Ein Objekt beim id-Selektor, sonst Array mit tatsächlichen limit-Listenzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"exportmanager_012345abcdef_123456","name":"Kontobezeichnung","description":"Beispieldefinition","enabled":true,"available_in_offer":false,"definition":{"name":"Kontobezeichnung","description":"Beispieldefinition","contexts":["global"],"grain":"account","inputs":[],"filters":[],"columns":[{"field":"account.name","key":"account_name","label":"Organisation"}],"enabled":true,"available_in_offer":false},"definition_version":1,"created_at":1792627200,"updated_at":1792627200}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Exportmanager.php","line":61},"reason":"Read definitions, catalogs, existing run status or completed output; no job processing in these GET branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"getExportmanager","line":61},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/exportmanager/{id} Individuelle Exportdefinition ersetzen Speichert eine vollständige validierte Definition. name und mindestens eine gültige columns-Spalte sind erforderlich; Inputs, Filter und grain müssen zu Katalog und Datenabhängigkeiten passen. Metadaten können in data oder data.definition stehen; äußere Metadaten überschreiben innere. contexts und available_in_offer werden neu abgeleitet. PUT ersetzt vollständig und ist kein partielles Patch. - Operation-ID: put_v2_exportmanager_id - Dokumentation: /docs/api/v2/operations/put-v2-exportmanager-id-8679787063d3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche exportmanager-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche exportmanager-ID.","schema":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Angebotsliste\",\"grain\":\"offer\",\"columns\":[{\"field\":\"offer.id\",\"label\":\"ID\"},{\"field\":\"offer.name\",\"label\":\"Angebot\"}],\"inputs\":[{\"field\":\"offer.id\",\"mode\":\"runtime\"}],\"filters\":[],\"enabled\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_exportmanager_id","summary":"Individuelle Exportdefinition ersetzen","description":"Speichert eine vollständige validierte Definition. name und mindestens eine gültige columns-Spalte sind erforderlich; Inputs, Filter und grain müssen zu Katalog und Datenabhängigkeiten passen. Metadaten können in data oder data.definition stehen; äußere Metadaten überschreiben innere. contexts und available_in_offer werden neu abgeleitet. PUT ersetzt vollständig und ist kein partielles Patch.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche exportmanager-ID.","schema":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"exportmanager_012345abcdef_123456","export":{"id":"exportmanager_012345abcdef_123456","name":"Kontobezeichnung","description":"Beispieldefinition","enabled":true,"available_in_offer":false,"definition":{"name":"Kontobezeichnung","description":"Beispieldefinition","contexts":["global"],"grain":"account","inputs":[],"filters":[],"columns":[{"field":"account.name","key":"account_name","label":"Organisation"}],"enabled":true,"available_in_offer":false},"definition_version":1,"created_at":1792627200,"updated_at":1792627200}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"export":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100,"minItems":1},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte.","required":["name","columns"]},{"type":"object","properties":{"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"required":["definition"]}]},"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Individuelle Exportdefinition ersetzen","value":{"data":{"name":"Angebotsliste","grain":"offer","columns":[{"field":"offer.id","label":"ID"},{"field":"offer.name","label":"Angebot"}],"inputs":[{"field":"offer.id","mode":"runtime"}],"filters":[],"enabled":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"updateExportmanager","line":183},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/exportmanager/list Gespeicherte individuelle Exportdefinitionen lesen Liest eine Definition per Pfad-ID bzw. id-Filter oder alle nicht gelöschten Definitionen nach Name sortiert. available_in_offer filtert die Angebotsverfügbarkeit; true beschränkt zusätzlich auf aktive Definitionen. Andere generische Filter, Feldauswahl, Sortierung und Pagination werden ignoriert. - Operation-ID: get_v2_exportmanager_list - Dokumentation: /docs/api/v2/operations/get-v2-exportmanager-list-9fa16dcc28e1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | run | query | nein | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","available_in_offer"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"available_in_offer","value":true}]}}},{"name":"run","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_exportmanager_list","summary":"Gespeicherte individuelle Exportdefinitionen lesen","description":"Liest eine Definition per Pfad-ID bzw. id-Filter oder alle nicht gelöschten Definitionen nach Name sortiert. available_in_offer filtert die Angebotsverfügbarkeit; true beschränkt zusätzlich auf aktive Definitionen. Andere generische Filter, Feldauswahl, Sortierung und Pagination werden ignoriert.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","available_in_offer"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"available_in_offer","value":true}]}}},{"name":"run","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Ein Objekt beim id-Selektor, sonst Array mit tatsächlichen limit-Listenzahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"exportmanager_012345abcdef_123456","name":"Kontobezeichnung","description":"Beispieldefinition","enabled":true,"available_in_offer":false,"definition":{"name":"Kontobezeichnung","description":"Beispieldefinition","contexts":["global"],"grain":"account","inputs":[],"filters":[],"columns":[{"field":"account.name","key":"account_name","label":"Organisation"}],"enabled":true,"available_in_offer":false},"definition_version":1,"created_at":1792627200,"updated_at":1792627200}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"available_in_offer":{"type":"boolean"},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"definition_version":{"type":"integer"},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Exportmanager.php","line":61},"reason":"Read definitions, catalogs, existing run status or completed output; no job processing in these GET branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"getExportmanager","line":61},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/exportmanager/list/analyze Exportentwurf und Datenabhängigkeiten prüfen Prüft einen Entwurf auf erlaubte Felder, Datenpfade, Ergebnisstruktur und fehlende Runtime-Inputs. Liefert Fehler, Warnungen, Empfehlungen, erreichbare Gruppen und can_preview. Unvollständige Entwürfe erzeugen Analysefehler im Ergebnis; es wird nichts gespeichert. Ohne Body wird ein leerer Entwurf analysiert und fachlich nicht ausführbar zurückgegeben. - Operation-ID: post_v2_exportmanager_list_analyze - Dokumentation: /docs/api/v2/operations/post-v2-exportmanager-list-analyze-87a67bb6144b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true, "runtime_inputs": { "offer.id": "offer_012345abcdef_123456" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/list/analyze' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/list/analyze"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true, "runtime_inputs": { "offer.id": "offer_012345abcdef_123456" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/list/analyze" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Angebotsliste\",\"grain\":\"offer\",\"columns\":[{\"field\":\"offer.id\",\"label\":\"ID\"},{\"field\":\"offer.name\",\"label\":\"Angebot\"}],\"inputs\":[{\"field\":\"offer.id\",\"mode\":\"runtime\"}],\"filters\":[],\"enabled\":true,\"runtime_inputs\":{\"offer.id\":\"offer_012345abcdef_123456\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_exportmanager_list_analyze","summary":"Exportentwurf und Datenabhängigkeiten prüfen","description":"Prüft einen Entwurf auf erlaubte Felder, Datenpfade, Ergebnisstruktur und fehlende Runtime-Inputs. Liefert Fehler, Warnungen, Empfehlungen, erreichbare Gruppen und can_preview. Unvollständige Entwürfe erzeugen Analysefehler im Ergebnis; es wird nichts gespeichert. Ohne Body wird ein leerer Entwurf analysiert und fachlich nicht ausführbar zurückgegeben.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Analyseobjekt ohne normalisierte definition.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"valid":true,"active_groups":["account"],"reachable_groups":["account"],"required_nodes":["account"],"suggested_grain":"account","grain":"account","warnings":[],"recommendations":[],"errors":[],"can_preview":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"valid":{"type":"boolean"},"active_groups":{"type":"array","items":{"type":"string"}},"reachable_groups":{"type":"array","items":{"type":"string"}},"required_nodes":{"type":"array","items":{"type":"string"}},"suggested_grain":{"type":"string"},"grain":{"type":"string"},"warnings":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"additionalProperties":true}},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"additionalProperties":true}},"recommendations":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"can_preview":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"runtime_inputs":{"type":"object","additionalProperties":{"type":["string","number","boolean"]}}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."}},"additionalProperties":true},"examples":{"request":{"summary":"Exportentwurf und Datenabhängigkeiten prüfen","value":{"data":{"name":"Angebotsliste","grain":"offer","columns":[{"field":"offer.id","label":"ID"},{"field":"offer.name","label":"Angebot"}],"inputs":[{"field":"offer.id","mode":"runtime"}],"filters":[],"enabled":true,"runtime_inputs":{"offer.id":"offer_012345abcdef_123456"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"postExportmanager","line":117,"branch":{"file":"application/models/v2/Exportmanager.php","line":125}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/exportmanager/list/catalog Felder und Eingaben individueller Exporte entdecken Liest erlaubte Exportfelder nach Datenbereich sowie Inputs, Filteroperatoren und Ergebnisstrukturen. Zusatzfeldbezeichnungen stammen möglichst aus dem Konto. SQL, Tabellen und interne IDs werden nicht ausgegeben. - Operation-ID: get_v2_exportmanager_list_catalog - Dokumentation: /docs/api/v2/operations/get-v2-exportmanager-list-catalog-d42de9d37e09/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | run | query | nein | Query input read by this resource or a delegated helper; only the relevant action consumes it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"run","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/list/catalog' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/list/catalog"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/list/catalog" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_exportmanager_list_catalog","summary":"Felder und Eingaben individueller Exporte entdecken","description":"Liest erlaubte Exportfelder nach Datenbereich sowie Inputs, Filteroperatoren und Ergebnisstrukturen. Zusatzfeldbezeichnungen stammen möglichst aus dem Konto. SQL, Tabellen und interne IDs werden nicht ausgegeben.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"run","in":"query","required":false,"description":"Query input read by this resource or a delegated helper; only the relevant action consumes it.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"groups":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"group":{"type":"string"},"type":{"type":"string"},"filterable":{"type":"boolean"},"selectable":{"type":"boolean"}},"additionalProperties":true}}},"additionalProperties":true}},"inputs":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"filters":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"operators":{"type":"array","items":{"type":"string"}},"grains":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Exportmanager.php","line":61},"reason":"Read definitions, catalogs, existing run status or completed output; no job processing in these GET branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"getExportmanager","line":61,"branch":{"file":"application/models/v2/Exportmanager.php","line":66}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/exportmanager/list/download Fertige CSV-Exportdatei herunterladen Liest ausschließlich einen Lauf des authentifizierten Kontos und Administrators. Nur abgeschlossene Läufe mit vorhandener Datei und weiterhin gültigen Quellberechtigungen sind ladbar. Die Antwort ist der CSV-Dateiinhalt als Download. Der konfigurierte Download-MIME-Typ ist text/x-comma-separated-values; Dateigröße und Attachment-Dateiname stehen in den Response-Headern. - Operation-ID: get_v2_exportmanager_list_download - Dokumentation: /docs/api/v2/operations/get-v2-exportmanager-list-download-d2b559e35659/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | run | query | ja | Lauf-ID aus POST /exportmanager/list/run. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"run","in":"query","required":true,"schema":{"type":"string","pattern":"^exportrun_[a-f0-9]{12}_-?[0-9]+$","examples":["exportrun_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"example":"exportrun_012345abcdef_123456","description":"Lauf-ID aus POST /exportmanager/list/run."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_RUN:?QUERY_RUN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/list/download' \ --url-query 'run='"${QUERY_RUN}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/list/download"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("run", required("QUERY_RUN")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/list/download" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("run", os.environ["QUERY_RUN"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_exportmanager_list_download","summary":"Fertige CSV-Exportdatei herunterladen","description":"Liest ausschließlich einen Lauf des authentifizierten Kontos und Administrators. Nur abgeschlossene Läufe mit vorhandener Datei und weiterhin gültigen Quellberechtigungen sind ladbar. Die Antwort ist der CSV-Dateiinhalt als Download. Der konfigurierte Download-MIME-Typ ist text/x-comma-separated-values; Dateigröße und Attachment-Dateiname stehen in den Response-Headern.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"run","in":"query","required":true,"schema":{"type":"string","pattern":"^exportrun_[a-f0-9]{12}_-?[0-9]+$","examples":["exportrun_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"example":"exportrun_012345abcdef_123456","description":"Lauf-ID aus POST /exportmanager/list/run."}],"responses":{"200":{"description":"CSV-Dateibytes eines abgeschlossenen eigenen Exportlaufs. force_download verwendet nach Quell-, Eigentums- und Pfadprüfung den konfigurierten MIME-Typ text/x-comma-separated-values. Fachliche Legacy-Fehler können als JSON mit error auftreten.","content":{"text/x-comma-separated-values":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"type":"object","properties":{"error":{"type":["string","boolean"]},"error_code":{"type":"string"}},"additionalProperties":true}}},"headers":{"Content-Disposition":{"schema":{"type":"string"},"description":"Attachment with sanitized filename and UTF-8 filename*."},"Content-Length":{"schema":{"type":"integer","minimum":0}},"Cache-Control":{"schema":{"type":"string"},"example":"private, no-transform, no-store, must-revalidate"}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-contract-evidence":[{"file":"application/models/v2/Exportmanager.php","line":449},{"file":"system/helpers/download_helper.php","line":110},{"file":"application/config/mimes.php","line":15}],"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Exportmanager.php","line":61},"reason":"Read definitions, catalogs, existing run status or completed output; no job processing in these GET branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"getExportmanager","line":61,"branch":{"file":"application/models/v2/Exportmanager.php","line":72}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/exportmanager/list/preview Exportvorschau lesen Prüft einen Entwurf auf erlaubte Felder, Datenpfade, Ergebnisstruktur und fehlende Runtime-Inputs. Alternativ wählt export eine gespeicherte Definition. Bei ausführbarer Definition werden nach Berechtigungsprüfung höchstens zehn Zeilen gelesen; sonst leere rows mit Analysehinweisen. Speichert weder Definition noch Lauf. Ohne Body wird ein leerer Entwurf analysiert und fachlich nicht ausführbar zurückgegeben. - Operation-ID: post_v2_exportmanager_list_preview - Dokumentation: /docs/api/v2/operations/post-v2-exportmanager-list-preview-c33f7022a988/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true, "runtime_inputs": { "offer.id": "offer_012345abcdef_123456" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/list/preview' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/list/preview"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Angebotsliste", "grain": "offer", "columns": [ { "field": "offer.id", "label": "ID" }, { "field": "offer.name", "label": "Angebot" } ], "inputs": [ { "field": "offer.id", "mode": "runtime" } ], "filters": [], "enabled": true, "runtime_inputs": { "offer.id": "offer_012345abcdef_123456" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/list/preview" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Angebotsliste\",\"grain\":\"offer\",\"columns\":[{\"field\":\"offer.id\",\"label\":\"ID\"},{\"field\":\"offer.name\",\"label\":\"Angebot\"}],\"inputs\":[{\"field\":\"offer.id\",\"mode\":\"runtime\"}],\"filters\":[],\"enabled\":true,\"runtime_inputs\":{\"offer.id\":\"offer_012345abcdef_123456\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_exportmanager_list_preview","summary":"Exportvorschau lesen","description":"Prüft einen Entwurf auf erlaubte Felder, Datenpfade, Ergebnisstruktur und fehlende Runtime-Inputs. Alternativ wählt export eine gespeicherte Definition. Bei ausführbarer Definition werden nach Berechtigungsprüfung höchstens zehn Zeilen gelesen; sonst leere rows mit Analysehinweisen. Speichert weder Definition noch Lauf. Ohne Body wird ein leerer Entwurf analysiert und fachlich nicht ausführbar zurückgegeben.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Vorschau mit columns, rows, has_more, analysis und öffentlichem plan.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"key":"account_name","field":"account.name","label":"Organisation","type":"string"}],"rows":[{"account_name":"Beispielorganisation"}],"has_more":false,"analysis":{"valid":true,"active_groups":["account"],"reachable_groups":["account"],"required_nodes":["account"],"suggested_grain":"account","grain":"account","warnings":[],"recommendations":[],"errors":[],"can_preview":true},"plan":{"grain":"account","groups":["account"]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"field":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true}},"rows":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"has_more":{"type":"boolean"},"analysis":{"type":"object","properties":{"valid":{"type":"boolean"},"active_groups":{"type":"array","items":{"type":"string"}},"reachable_groups":{"type":"array","items":{"type":"string"}},"required_nodes":{"type":"array","items":{"type":"string"}},"suggested_grain":{"type":"string"},"grain":{"type":"string"},"warnings":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"additionalProperties":true}},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"additionalProperties":true}},"recommendations":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"can_preview":{"type":"boolean"}},"additionalProperties":true},"plan":{"anyOf":[{"type":"object","properties":{"grain":{"type":"string"},"groups":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},{"type":"null"}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"definition":{"type":"object","properties":{"name":{"type":"string","description":"Zum Speichern nichtleer; Kürzung auf 160 Zeichen."},"description":{"type":"string","description":"Kürzung auf 4000 Zeichen."},"grain":{"type":"string","enum":["auto","account","offer","booking_user","date_admin"],"default":"auto"},"columns":{"type":"array","items":{"anyOf":[{"type":"string","description":"Selektierbarer catalog-Feldschlüssel."},{"type":"object","properties":{"field":{"type":"string"},"label":{"type":"string","description":"Kürzung auf 160 Zeichen."}},"additionalProperties":true,"required":["field"]}]},"maxItems":100},"inputs":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"catalog-Inputfeld, z. B. offer.id."},"key":{"type":"string","description":"Alias für field."},"mode":{"type":"string","enum":["runtime","fixed","context"],"default":"runtime"},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"description":"fixed benötigt value. context ignoriert gespeichertes value; runtime/context werden über runtime_inputs bereitgestellt."},"maxItems":10},"filters":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Filterbarer catalog-Feldschlüssel."},"operator":{"type":"string","description":"Feldabhängiger catalog-Operator, Standard equal."},"value":{"type":["string","number","boolean"]}},"additionalProperties":true,"required":["field"],"description":"value ist außer für is_empty/is_not_empty erforderlich und darf nicht leer sein."},"maxItems":30},"enabled":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"contexts":{"type":"array","items":{"type":"string"}},"available_in_offer":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."},"runtime_inputs":{"type":"object","additionalProperties":{"type":["string","number","boolean"]}},"export":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"description":"Nur Katalogfelder sind erlaubt. contexts und available_in_offer werden aus Inputs neu abgeleitet; unbekannte Eigenschaften verworfen. Gültige Definitionen benötigen mindestens eine gültige Spalte."}},"additionalProperties":true},"examples":{"request":{"summary":"Exportvorschau lesen","value":{"data":{"name":"Angebotsliste","grain":"offer","columns":[{"field":"offer.id","label":"ID"},{"field":"offer.name","label":"Angebot"}],"inputs":[{"field":"offer.id","mode":"runtime"}],"filters":[],"enabled":true,"runtime_inputs":{"offer.id":"offer_012345abcdef_123456"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"postExportmanager","line":117,"branch":{"file":"application/models/v2/Exportmanager.php","line":128}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/exportmanager/list/run Individuellen CSV-Export als Hintergrundlauf starten Startet einen Lauf einer aktiven gespeicherten Definition. request_key dedupliziert pro Konto und Administrator; derselbe Schlüssel liefert den bestehenden Lauf. Runtime-/Context-Inputs, Quellrechte sowie Kapazitäts-/Ressourcenlimits werden geprüft. Schreibt Lauf und Queue; danach run_status lesen. - Operation-ID: post_v2_exportmanager_list_run - Dokumentation: /docs/api/v2/operations/post-v2-exportmanager-list-run-b180a3d2134b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "export": "exportmanager_012345abcdef_123456", "request_key": "export-example-20261022", "format": "csv", "runtime_inputs": { "offer.id": "offer_012345abcdef_123456" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/list/run' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/list/run"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "export": "exportmanager_012345abcdef_123456", "request_key": "export-example-20261022", "format": "csv", "runtime_inputs": { "offer.id": "offer_012345abcdef_123456" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/list/run" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"export\":\"exportmanager_012345abcdef_123456\",\"request_key\":\"export-example-20261022\",\"format\":\"csv\",\"runtime_inputs\":{\"offer.id\":\"offer_012345abcdef_123456\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_exportmanager_list_run","summary":"Individuellen CSV-Export als Hintergrundlauf starten","description":"Startet einen Lauf einer aktiven gespeicherten Definition. request_key dedupliziert pro Konto und Administrator; derselbe Schlüssel liefert den bestehenden Lauf. Runtime-/Context-Inputs, Quellrechte sowie Kapazitäts-/Ressourcenlimits werden geprüft. Schreibt Lauf und Queue; danach run_status lesen.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Synchroner HTTP-200-JSON-Umschlag des asynchronen Exportlaufs bzw. seines aktuellen Zustands. Dieser Quellpfad antwortet nicht mit HTTP 202.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["run","export","status","progress","row_count","format","created_at","started_at","completed_at"],"properties":{"run":{"type":"string","pattern":"^exportrun_[a-f0-9]{12}_-?[0-9]+$","examples":["exportrun_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"export":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"status":{"enum":["queued","running","completed","empty","failed"]},"progress":{"type":"integer","minimum":0,"maximum":100},"row_count":{"type":"integer","minimum":0},"format":{"const":"csv"},"created_at":{"type":"integer"},"started_at":{"type":"integer"},"completed_at":{"type":"integer"},"download_url":{"type":"string","format":"uri","description":"Exposed only for a completed run with requester/source rights and an existing owned secure export file."},"error_code":{"enum":["custom_export_failed","no_matching_records"]}},"additionalProperties":true,"description":"Exportmanager::serializeRun: Die neun Pflichtfelder werden immer geliefert. finalizing erscheint öffentlich als running; ein Download-Link wird nur bei completed und weiter bestehenden Berechtigungen ausgegeben."},"error":{"type":["boolean","string"]},"success":{"type":"boolean"},"type":{"type":"string"}},"additionalProperties":true},"example":{"data":{"run":"exportrun_012345abcdef_123456","export":"exportmanager_012345abcdef_123456","status":"queued","progress":0,"row_count":0,"format":"csv","created_at":1792627200,"started_at":0,"completed_at":0},"error":false,"success":true,"type":"exportmanager"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"export":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"request_key":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_-]{15,79}$","minLength":16,"maxLength":80},"format":{"type":"string","description":"Nach Trimmen/Kleinschreibung nur csv; Standard csv."},"runtime_inputs":{"anyOf":[{"type":"object","additionalProperties":{"type":["string","number","boolean"]},"maxProperties":10},{"type":"string","description":"Beim run alternativ eine JSON-kodierte Map."}],"description":"Deklarierte Runtime-/Context-Feldschlüssel auf öffentliche v2-IDs dieses Kontos. Alle nicht festen Inputs brauchen Werte; feste Inputs dürfen nicht überschrieben werden."}},"additionalProperties":true,"required":["export","request_key"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Individuellen CSV-Export als Hintergrundlauf starten","value":{"data":{"export":"exportmanager_012345abcdef_123456","request_key":"export-example-20261022","format":"csv","runtime_inputs":{"offer.id":"offer_012345abcdef_123456"}}}}}}}},"x-edoobox-contract-evidence":[{"file":"application/models/v2/Exportmanager.php","line":262},{"file":"application/models/v2/Exportmanager.php","line":1549}],"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"postExportmanager","line":117,"branch":{"file":"application/models/v2/Exportmanager.php","line":122}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/exportmanager/list/run_status Exportlauf-Fortschritt lesen Liest ausschließlich einen Lauf des authentifizierten Kontos und Administrators. Liefert Status, Fortschritt, Zeilenzahl und nach Abschluss eine Download-URL, sofern Datei und Quellzugriff weiterhin verfügbar sind. - Operation-ID: get_v2_exportmanager_list_run_status - Dokumentation: /docs/api/v2/operations/get-v2-exportmanager-list-run-status-ecf37ded81ab/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | run | query | ja | Lauf-ID aus POST /exportmanager/list/run. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"run","in":"query","required":true,"schema":{"type":"string","pattern":"^exportrun_[a-f0-9]{12}_-?[0-9]+$","examples":["exportrun_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"example":"exportrun_012345abcdef_123456","description":"Lauf-ID aus POST /exportmanager/list/run."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_RUN:?QUERY_RUN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/exportmanager/list/run_status' \ --url-query 'run='"${QUERY_RUN}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/exportmanager/list/run_status"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("run", required("QUERY_RUN")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/exportmanager/list/run_status" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("run", os.environ["QUERY_RUN"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_exportmanager_list_run_status","summary":"Exportlauf-Fortschritt lesen","description":"Liest ausschließlich einen Lauf des authentifizierten Kontos und Administrators. Liefert Status, Fortschritt, Zeilenzahl und nach Abschluss eine Download-URL, sofern Datei und Quellzugriff weiterhin verfügbar sind.","tags":["exportmanager"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"run","in":"query","required":true,"schema":{"type":"string","pattern":"^exportrun_[a-f0-9]{12}_-?[0-9]+$","examples":["exportrun_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"example":"exportrun_012345abcdef_123456","description":"Lauf-ID aus POST /exportmanager/list/run."}],"responses":{"200":{"description":"Synchroner HTTP-200-JSON-Umschlag des asynchronen Exportlaufs bzw. seines aktuellen Zustands. Dieser Quellpfad antwortet nicht mit HTTP 202.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["run","export","status","progress","row_count","format","created_at","started_at","completed_at"],"properties":{"run":{"type":"string","pattern":"^exportrun_[a-f0-9]{12}_-?[0-9]+$","examples":["exportrun_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"export":{"type":"string","pattern":"^exportmanager_[a-f0-9]{12}_-?[0-9]+$","examples":["exportmanager_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"status":{"enum":["queued","running","completed","empty","failed"]},"progress":{"type":"integer","minimum":0,"maximum":100},"row_count":{"type":"integer","minimum":0},"format":{"const":"csv"},"created_at":{"type":"integer"},"started_at":{"type":"integer"},"completed_at":{"type":"integer"},"download_url":{"type":"string","format":"uri","description":"Exposed only for a completed run with requester/source rights and an existing owned secure export file."},"error_code":{"enum":["custom_export_failed","no_matching_records"]}},"additionalProperties":true,"description":"Exportmanager::serializeRun: Die neun Pflichtfelder werden immer geliefert. finalizing erscheint öffentlich als running; ein Download-Link wird nur bei completed und weiter bestehenden Berechtigungen ausgegeben."},"error":{"type":["boolean","string"]},"success":{"type":"boolean"},"type":{"type":"string"}},"additionalProperties":true},"example":{"data":{"run":"exportrun_012345abcdef_123456","export":"exportmanager_012345abcdef_123456","status":"queued","progress":0,"row_count":0,"format":"csv","created_at":1792627200,"started_at":0,"completed_at":0},"error":false,"success":true,"type":"exportmanager"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-contract-evidence":[{"file":"application/models/v2/Exportmanager.php","line":262},{"file":"application/models/v2/Exportmanager.php","line":1549}],"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Exportmanager.php","line":61},"reason":"Read definitions, catalogs, existing run status or completed output; no job processing in these GET branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Exportmanager.php","method":"getExportmanager","line":61,"branch":{"file":"application/models/v2/Exportmanager.php","line":69}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"exportmanager","short":"expm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/fee Gebühren und offene Beträge abrufen Liest Gebühren mit Buchungs-, Rechnungs-, Zahlungs- und Rückerstattungsbezug sowie Regelsnapshot. Ein gültiger id-Selektor liefert ein Objekt, sonst eine ID-Map. Nur booking, user und transaction begrenzen die Liste; Reihenfolge ist absteigend. Eine nicht dekodierbare ID kann zur Liste führen. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt. - Operation-ID: get_v2_fee - Dokumentation: /docs/api/v2/operations/get-v2-fee-4afbae84e222/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | query | nein | Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","booking","user","transaction"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"booking","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/fee' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_fee","summary":"Gebühren und offene Beträge abrufen","description":"Liest Gebühren mit Buchungs-, Rechnungs-, Zahlungs- und Rückerstattungsbezug sowie Regelsnapshot. Ein gültiger id-Selektor liefert ein Objekt, sonst eine ID-Map. Nur booking, user und transaction begrenzen die Liste; Reihenfolge ist absteigend. Eine nicht dekodierbare ID kann zur Liste führen. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","booking","user","transaction"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"booking","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Gebührenobjekt oder ID-Map; eine leere PHP-Map wird []. Geldbeträge sind Zahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"fee_012345abcdef_123456":{"id":"fee_012345abcdef_123456","transaction":"transaction_012345abcdef_123456","type":"cancellation","status":"open","amount":25,"open_amount":25,"currency":"CHF","vat":false,"booking":"booking_012345abcdef_123456","offer":"offer_012345abcdef_123456","user":"user_012345abcdef_123456","source_transaction":"transaction_012345abcdef_123456","invoice":false,"payment":false,"refund":false,"parent_fee":false,"dunning_level":0,"rule_snapshot":false,"reason":"","created_at":0,"updated_at":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"getFee","line":25},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/fee/{id} Gebühren und offene Beträge abrufen Liest Gebühren mit Buchungs-, Rechnungs-, Zahlungs- und Rückerstattungsbezug sowie Regelsnapshot. Ein gültiger id-Selektor liefert ein Objekt, sonst eine ID-Map. Nur booking, user und transaction begrenzen die Liste; Reihenfolge ist absteigend. Eine nicht dekodierbare ID kann zur Liste führen. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt. - Operation-ID: get_v2_fee_id - Dokumentation: /docs/api/v2/operations/get-v2-fee-id-23193f431ade/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche fee-ID. | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | query | nein | Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche fee-ID.","schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","booking","user","transaction"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"booking","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/fee/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_fee_id","summary":"Gebühren und offene Beträge abrufen","description":"Liest Gebühren mit Buchungs-, Rechnungs-, Zahlungs- und Rückerstattungsbezug sowie Regelsnapshot. Ein gültiger id-Selektor liefert ein Objekt, sonst eine ID-Map. Nur booking, user und transaction begrenzen die Liste; Reihenfolge ist absteigend. Eine nicht dekodierbare ID kann zur Liste führen. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche fee-ID.","schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","booking","user","transaction"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"booking","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID."}],"responses":{"200":{"description":"Gebührenobjekt oder ID-Map; eine leere PHP-Map wird []. Geldbeträge sind Zahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"fee_012345abcdef_123456","transaction":"transaction_012345abcdef_123456","type":"cancellation","status":"open","amount":25,"open_amount":25,"currency":"CHF","vat":false,"booking":"booking_012345abcdef_123456","offer":"offer_012345abcdef_123456","user":"user_012345abcdef_123456","source_transaction":"transaction_012345abcdef_123456","invoice":false,"payment":false,"refund":false,"parent_fee":false,"dunning_level":0,"rule_snapshot":false,"reason":"","created_at":0,"updated_at":0}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/14","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":254,"reason":"APIPermissions resource mapping to fee.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Reads existing fee records and converts their stored snapshots/relations to public IDs; it does not assess, cancel, invoice or remind fees.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/14","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"getFee","line":25},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/fee/{id}/cancel Gebühr stornieren oder gegenbuchen Storniert offene Gebühren oder erzeugt eine Gegenbuchung für bereits abgerechnete Gebühren. reason ist ein optionaler administrativer Vermerk. Bereits stornierte Gebühren bleiben unverändert. Extern geführte Rechnungen und laufende Buchhaltungsaktionen können blockieren; error=false allein bestätigt keine Durchführung. Bei bereits bezahlten Gebühren kann der Aufruf außerdem eine Rückerstattung anstoßen. - Operation-ID: put_v2_fee_id_cancel - Dokumentation: /docs/api/v2/operations/put-v2-fee-id-cancel-1e05d0729f36/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche fee-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Öffentliche fee-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "reason": "Kulanz nach Prüfung" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/fee/'"${PATH_ID}"'/cancel' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee/{id}/cancel"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "reason": "Kulanz nach Prüfung" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee/{id}/cancel" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"reason\":\"Kulanz nach Prüfung\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_fee_id_cancel","summary":"Gebühr stornieren oder gegenbuchen","description":"Storniert offene Gebühren oder erzeugt eine Gegenbuchung für bereits abgerechnete Gebühren. reason ist ein optionaler administrativer Vermerk. Bereits stornierte Gebühren bleiben unverändert. Extern geführte Rechnungen und laufende Buchhaltungsaktionen können blockieren; error=false allein bestätigt keine Durchführung. Bei bereits bezahlten Gebühren kann der Aufruf außerdem eine Rückerstattung anstoßen.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Öffentliche fee-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"fee_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"},"blocked_reason":{"type":["string","boolean"]},"accounting_operation_in_progress":{"type":"boolean"},"accounting_manual_review":{"type":"boolean"},"reason":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"reason":{"type":"string"}},"additionalProperties":true},"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"Gebühr stornieren oder gegenbuchen","value":{"data":{"reason":"Kulanz nach Prüfung"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Fee.php","line":79,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"updateFee","line":69,"branch":{"file":"application/models/v2/Fee.php","line":79}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/fee/{id}/invoice Gebührenrechnung erstellen Erstellt oder verwendet eine vorhandene Gebührenrechnung und verrechnet gegebenenfalls einbehaltene Rückerstattungen. Buchhaltungsstatus und laufende Aktionen können die Durchführung verhindern. - Operation-ID: put_v2_fee_id_invoice - Dokumentation: /docs/api/v2/operations/put-v2-fee-id-invoice-3b4d78f5e1c5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche fee-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Öffentliche fee-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/fee/'"${PATH_ID}"'/invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee/{id}/invoice"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee/{id}/invoice" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_fee_id_invoice","summary":"Gebührenrechnung erstellen","description":"Erstellt oder verwendet eine vorhandene Gebührenrechnung und verrechnet gegebenenfalls einbehaltene Rückerstattungen. Buchhaltungsstatus und laufende Aktionen können die Durchführung verhindern.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Öffentliche fee-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"fee_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"},"blocked_reason":{"type":["string","boolean"]},"accounting_operation_in_progress":{"type":"boolean"},"accounting_manual_review":{"type":"boolean"},"reason":{"type":"string"},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true},"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"Gebührenrechnung erstellen","value":{"data":{}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Fee.php","line":79,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"updateFee","line":69,"branch":{"file":"application/models/v2/Fee.php","line":79}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/fee/{id}/send_payment_reminder Zahlungserinnerung zur Gebühr versenden Erstellt eine Zahlungserinnerungs-E-Mail an den Gebührenkunden. Erfordert eine zahlbare Gebühr mit offenem Betrag und gültiger Empfängeradresse. data.send und blocked_reason zeigen, ob der Versand angestoßen wurde. - Operation-ID: put_v2_fee_id_send_payment_reminder - Dokumentation: /docs/api/v2/operations/put-v2-fee-id-send-payment-reminder-f6129093d327/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche fee-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Öffentliche fee-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/fee/'"${PATH_ID}"'/send_payment_reminder' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee/{id}/send_payment_reminder"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee/{id}/send_payment_reminder" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_fee_id_send_payment_reminder","summary":"Zahlungserinnerung zur Gebühr versenden","description":"Erstellt eine Zahlungserinnerungs-E-Mail an den Gebührenkunden. Erfordert eine zahlbare Gebühr mit offenem Betrag und gültiger Empfängeradresse. data.send und blocked_reason zeigen, ob der Versand angestoßen wurde.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"description":"Öffentliche fee-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"fee_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"send":{"type":"boolean"},"blocked_reason":{"type":["string","boolean"]},"accounting_operation_in_progress":{"type":"boolean"},"accounting_manual_review":{"type":"boolean"},"reason":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true},"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true},"examples":{"request":{"summary":"Zahlungserinnerung zur Gebühr versenden","value":{"data":{}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Fee.php","line":140,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"updateFee","line":69,"branch":{"file":"application/models/v2/Fee.php","line":140}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/fee/list Gebühren und offene Beträge abrufen Liest Gebühren mit Buchungs-, Rechnungs-, Zahlungs- und Rückerstattungsbezug sowie Regelsnapshot. Ein gültiger id-Selektor liefert ein Objekt, sonst eine ID-Map. Nur booking, user und transaction begrenzen die Liste; Reihenfolge ist absteigend. Eine nicht dekodierbare ID kann zur Liste führen. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt. - Operation-ID: get_v2_fee_list - Dokumentation: /docs/api/v2/operations/get-v2-fee-list-6595c8f1b49e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | query | nein | Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","booking","user","transaction"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"booking","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/fee/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_fee_list","summary":"Gebühren und offene Beträge abrufen","description":"Liest Gebühren mit Buchungs-, Rechnungs-, Zahlungs- und Rückerstattungsbezug sowie Regelsnapshot. Ein gültiger id-Selektor liefert ein Objekt, sonst eine ID-Map. Nur booking, user und transaction begrenzen die Liste; Reihenfolge ist absteigend. Eine nicht dekodierbare ID kann zur Liste führen. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id","booking","user","transaction"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"booking","value":"booking_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Öffentliche Gebühren-v2-ID."}],"responses":{"200":{"description":"Gebührenobjekt oder ID-Map; eine leere PHP-Map wird []. Geldbeträge sind Zahlen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"fee_012345abcdef_123456":{"id":"fee_012345abcdef_123456","transaction":"transaction_012345abcdef_123456","type":"cancellation","status":"open","amount":25,"open_amount":25,"currency":"CHF","vat":false,"booking":"booking_012345abcdef_123456","offer":"offer_012345abcdef_123456","user":"user_012345abcdef_123456","source_transaction":"transaction_012345abcdef_123456","invoice":false,"payment":false,"refund":false,"parent_fee":false,"dunning_level":0,"rule_snapshot":false,"reason":"","created_at":0,"updated_at":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"vat":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","examples":["vat_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"source_transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","examples":["transaction_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"payment":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","examples":["pay_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"refund":{"anyOf":[{"type":"string","pattern":"^refund_[a-f0-9]{12}_-?[0-9]+$","examples":["refund_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"parent_fee":{"anyOf":[{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]},"type":{"type":"string"},"status":{"type":"string"},"amount":{"type":"number"},"open_amount":{"type":"number"},"currency":{"type":"string"},"dunning_level":{"type":"integer"},"rule_snapshot":{"type":["object","array","boolean","null"],"additionalProperties":true},"reason":{"type":"string"},"created_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."},"updated_at":{"type":"integer","description":"Unveränderte Legacy-Ausgabe von dP(); Unix-Zeitwert oder 0 bei nicht parsebarem Eingabewert."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/13","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":254,"reason":"APIPermissions resource mapping to fee.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Reads existing fee records and converts their stored snapshots/relations to public IDs; it does not assess, cancel, invoice or remind fees.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/13","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"getFee","line":25},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/fee/list/cancel Gebühr stornieren oder gegenbuchen Storniert offene Gebühren oder erzeugt eine Gegenbuchung für bereits abgerechnete Gebühren. reason ist ein optionaler administrativer Vermerk. Bereits stornierte Gebühren bleiben unverändert. Extern geführte Rechnungen und laufende Buchhaltungsaktionen können blockieren; error=false allein bestätigt keine Durchführung. Bei bereits bezahlten Gebühren kann der Aufruf außerdem eine Rückerstattung anstoßen. - Operation-ID: put_v2_fee_list_cancel - Dokumentation: /docs/api/v2/operations/put-v2-fee-list-cancel-a4a6ddcc971b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "reason": "Kulanz nach Prüfung" }, "id": "fee_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/fee/list/cancel' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee/list/cancel"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "reason": "Kulanz nach Prüfung" }, "id": "fee_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee/list/cancel" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"reason\":\"Kulanz nach Prüfung\"},\"id\":\"fee_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_fee_list_cancel","summary":"Gebühr stornieren oder gegenbuchen","description":"Storniert offene Gebühren oder erzeugt eine Gegenbuchung für bereits abgerechnete Gebühren. reason ist ein optionaler administrativer Vermerk. Bereits stornierte Gebühren bleiben unverändert. Extern geführte Rechnungen und laufende Buchhaltungsaktionen können blockieren; error=false allein bestätigt keine Durchführung. Bei bereits bezahlten Gebühren kann der Aufruf außerdem eine Rückerstattung anstoßen.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"fee_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"},"blocked_reason":{"type":["string","boolean"]},"accounting_operation_in_progress":{"type":"boolean"},"accounting_manual_review":{"type":"boolean"},"reason":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"reason":{"type":"string"}},"additionalProperties":true},"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Gebühr stornieren oder gegenbuchen","value":{"data":{"reason":"Kulanz nach Prüfung"},"id":"fee_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"updateFee","line":69,"branch":{"file":"application/models/v2/Fee.php","line":79}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/fee/list/invoice Gebührenrechnung erstellen Erstellt oder verwendet eine vorhandene Gebührenrechnung und verrechnet gegebenenfalls einbehaltene Rückerstattungen. Buchhaltungsstatus und laufende Aktionen können die Durchführung verhindern. - Operation-ID: put_v2_fee_list_invoice - Dokumentation: /docs/api/v2/operations/put-v2-fee-list-invoice-e51b69c7d364/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {}, "id": "fee_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/fee/list/invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee/list/invoice"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {}, "id": "fee_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee/list/invoice" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{},\"id\":\"fee_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_fee_list_invoice","summary":"Gebührenrechnung erstellen","description":"Erstellt oder verwendet eine vorhandene Gebührenrechnung und verrechnet gegebenenfalls einbehaltene Rückerstattungen. Buchhaltungsstatus und laufende Aktionen können die Durchführung verhindern.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"fee_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"update":{"type":"boolean"},"blocked_reason":{"type":["string","boolean"]},"accounting_operation_in_progress":{"type":"boolean"},"accounting_manual_review":{"type":"boolean"},"reason":{"type":"string"},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":false}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true},"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Gebührenrechnung erstellen","value":{"data":{},"id":"fee_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"updateFee","line":69,"branch":{"file":"application/models/v2/Fee.php","line":79}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/fee/list/send_payment_reminder Zahlungserinnerung zur Gebühr versenden Erstellt eine Zahlungserinnerungs-E-Mail an den Gebührenkunden. Erfordert eine zahlbare Gebühr mit offenem Betrag und gültiger Empfängeradresse. data.send und blocked_reason zeigen, ob der Versand angestoßen wurde. - Operation-ID: put_v2_fee_list_send_payment_reminder - Dokumentation: /docs/api/v2/operations/put-v2-fee-list-send-payment-reminder-c77c270f80d2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {}, "id": "fee_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/fee/list/send_payment_reminder' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/fee/list/send_payment_reminder"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {}, "id": "fee_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/fee/list/send_payment_reminder" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{},\"id\":\"fee_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_fee_list_send_payment_reminder","summary":"Zahlungserinnerung zur Gebühr versenden","description":"Erstellt eine Zahlungserinnerungs-E-Mail an den Gebührenkunden. Erfordert eine zahlbare Gebühr mit offenem Betrag und gültiger Empfängeradresse. data.send und blocked_reason zeigen, ob der Versand angestoßen wurde.","tags":["fee"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"fee_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"send":{"type":"boolean"},"blocked_reason":{"type":["string","boolean"]},"accounting_operation_in_progress":{"type":"boolean"},"accounting_manual_review":{"type":"boolean"},"reason":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true},"id":{"type":"string","pattern":"^fee_[a-f0-9]{12}_-?[0-9]+$","examples":["fee_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Zahlungserinnerung zur Gebühr versenden","value":{"data":{},"id":"fee_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Fee.php","method":"updateFee","line":69,"branch":{"file":"application/models/v2/Fee.php","line":140}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"fee","short":"fee","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/file Teilnehmerdatei löschen Entfernt das Speicherobjekt, leert das zugeordnete Teilnehmer-Dateifeld und markiert die Dateimetadaten als gelöscht. Fehlende oder bereits entfernte aktive Datensätze ergeben delete=false; Speicherfehler werden als Fehler gemeldet. - Operation-ID: delete_v2_file - Dokumentation: /docs/api/v2/operations/delete-v2-file-e51a97cc15b7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | ja | Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":true,"schema":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"description":"Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_ID:?QUERY_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "file_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/file' \ --url-query 'id='"${QUERY_ID}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("id", required("QUERY_ID")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "file_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("id", os.environ["QUERY_ID"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"file_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_file","summary":"Teilnehmerdatei löschen","description":"Entfernt das Speicherobjekt, leert das zugeordnete Teilnehmer-Dateifeld und markiert die Dateimetadaten als gelöscht. Fehlende oder bereits entfernte aktive Datensätze ergeben delete=false; Speicherfehler werden als Fehler gemeldet.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"description":"Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete geben die angefragte Datei bzw. das Objekt und das Ergebnis an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"file_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Teilnehmerdatei löschen","value":{"id":"file_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"deleteFile","line":190},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/file Teilnehmerdatei auflisten Liest Metadaten der aktiven Teilnehmerdateien und deren Zuordnung zu user und datafield. Gelöschte Dateien sind ausgeschlossen; Dateiinhalte und Download-URLs liefert diese Route nicht. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_file - Dokumentation: /docs/api/v2/operations/get-v2-file-d83027c3b35c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/file' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_file","summary":"Teilnehmerdatei auflisten","description":"Liest Metadaten der aktiven Teilnehmerdateien und deren Zuordnung zu user und datafield. Gelöschte Dateien sind ausgeschlossen; Dateiinhalte und Download-URLs liefert diese Route nicht. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"file_012345abcdef_123456":{"id":"file_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","examples":["file_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"datafield":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":95,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","examples":["file_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"datafield":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":95,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"getFile","line":206},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/file Teilnehmerdatei anlegen Reserviert eine Teilnehmerdatei und liefert eine signierte Upload-URL. Das über user und datafield ausgewählte aktivierte Dateifeld darf noch keine aktive Datei enthalten; das Teilnehmerfeld wird bereits beim Reservieren aktualisiert. Datei-Bytes werden anschließend separat an upload_url übertragen. file_size ist eine Byte-Angabe im Request; die Metadaten speichern gerundete MiB, deren GET-Ausgabe zusätzlich zu int normalisiert wird. Die Erweiterung wird aus file_name abgeleitet; Datei-URLs sind vertraulich. - Operation-ID: post_v2_file - Dokumentation: /docs/api/v2/operations/post-v2-file-bc6b3b276c98/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "file_name": "example.pdf", "file_type": "application/pdf", "file_size": 128, "user": "user_012345abcdef_123456", "datafield": "data_1" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/file' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "file_name": "example.pdf", "file_type": "application/pdf", "file_size": 128, "user": "user_012345abcdef_123456", "datafield": "data_1" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"file_name\":\"example.pdf\",\"file_type\":\"application/pdf\",\"file_size\":128,\"user\":\"user_012345abcdef_123456\",\"datafield\":\"data_1\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_file","summary":"Teilnehmerdatei anlegen","description":"Reserviert eine Teilnehmerdatei und liefert eine signierte Upload-URL. Das über user und datafield ausgewählte aktivierte Dateifeld darf noch keine aktive Datei enthalten; das Teilnehmerfeld wird bereits beim Reservieren aktualisiert. Datei-Bytes werden anschließend separat an upload_url übertragen. file_size ist eine Byte-Angabe im Request; die Metadaten speichern gerundete MiB, deren GET-Ausgabe zusätzlich zu int normalisiert wird. Die Erweiterung wird aus file_name abgeleitet; Datei-URLs sind vertraulich.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert, öffentliche id und bei Uploads signierte upload_url; öffentliche Dateien liefern zusätzlich cdn_url.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"file_012345abcdef_123456","upload_url":"https://storage.example.invalid/synthetic-upload"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"insert":{"type":"boolean"},"upload_url":{"type":"string","format":"uri","description":"Signierte Upload-URL; vertraulich und zeitlich begrenzt."}},"additionalProperties":true,"required":["id","insert","upload_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["file_name","file_type","file_size","user","datafield"],"properties":{"file_name":{"type":"string","description":"The extension is lowercased from normalized file_name; supplied file_extension is ignored. The actual derived extension must have at most5 characters and contain no slash/backslash, C0/DEL or percent-encoded byte sequence. Private signed-storage names retain otherwise-safe ?, # and non-encoded percent punctuation."},"file_type":{"type":"string","maxLength":100,"description":"Native getter rejects raw UTF-8 length over100 and normalized SQL-escaped storage length over100. Quote/backslash-rich input can reach the encoded limit earlier than the raw limit."},"file_size":{"x-edoobox-canonicalType":"integer","description":"File byte count is PHP-int normalized; declared positive size is required by the upload workflow. The storage transfer independently verifies byte count.","examples":[128]},"user":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Existing tenant-bound public user v2 ID; never a database key."},"datafield":{"type":"string","description":"Existing enabled custom datafield configured with type=file in this account; e.g. data_1."},"file_extension":{"description":"Ignored by native create; the extension is derived from normalized file_name.","x-edoobox-ignored-input":true}},"additionalProperties":true,"description":"Source-specific single-record request; upstream account and validation checks apply."}},"additionalProperties":true},"examples":{"request":{"value":{"data":{"file_name":"example.pdf","file_type":"application/pdf","file_size":128,"user":"user_012345abcdef_123456","datafield":"data_1"}}}}}}},"x-edoobox-upload-ticket":{"provider":"s3","bucketEnv":"AWS_S3_BUCKET","regionEnv":"AWS_S3_REGION","urlPointer":"/data/upload_url","pathPrefixTemplate":"/ED{fid}F/","method":"PUT","expiresInSeconds":1800,"source":{"file":"application/models/v2/Filefunc.php","line":7,"sha256":"b0a20df1ad9c325900275984aea76e884aac74f86aa3a08be34da1fed06e87b1","method":"_postFile"},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":false,"scope":"object","readback":{"operationId":"get_v2_file_id","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"metadata-only"},"abort":{"operationId":"delete_v2_file_id","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"notes":["The metadata row is created before the storage PUT. No API completion or upload-status operation exists for this resource.","Metadata readback does not prove storage object existence or byte integrity. A ticket discard does not delete native metadata.","Abort is a separate mutation requiring normal preview, approval and current authorization. Never automatically retry a PUT after an ambiguous outcome.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-capability-source":{"file":"../edoobox-vue-2.0/src/store/file/moduleFileActions.js","line":75,"reason":"Participant datafield upload registers a file, then uploads bytes to the server-returned presigned URL."},"x-edoobox-upload-input-review":{"derivedExtensionLimit":5,"providedExtension":"ignored","nativeValidation":"The extension is lowercased from normalized file_name; supplied file_extension is ignored. The actual derived extension must have at most5 characters and contain no slash/backslash, C0/DEL or percent-encoded byte sequence. Private signed-storage names retain otherwise-safe ?, # and non-encoded percent punctuation.","source":{"file":"application/models/v2/Filefunc.php","line":36,"sha256":"b0a20df1ad9c325900275984aea76e884aac74f86aa3a08be34da1fed06e87b1","lineEnd":49},"relatedSource":{"file":"application/models/v2/File.php","line":94,"sha256":"3e44cb5fcf8fb7f9a57fc5fd11c53501ee655885dd432798fc3f88867a55a81f"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"postFile","line":156},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/file Teilnehmerdatei ändern Ändert den Anzeigenamen einer Teilnehmerdatei. Die Zuordnung zu user und datafield, MIME-Typ, Erweiterung und gespeicherte Dateigröße bleiben unveränderlich; Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_file - Dokumentation: /docs/api/v2/operations/put-v2-file-51a5213a899f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "file_name": "Unterlagen.pdf" }, "id": "file_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/file' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "file_name": "Unterlagen.pdf" }, "id": "file_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"file_name\":\"Unterlagen.pdf\"},\"id\":\"file_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_file","summary":"Teilnehmerdatei ändern","description":"Ändert den Anzeigenamen einer Teilnehmerdatei. Die Zuordnung zu user und datafield, MIME-Typ, Erweiterung und gespeicherte Dateigröße bleiben unveränderlich; Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"file_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"user":false,"datafield":false,"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":63},"file_extension":false,"file_type":false,"file_size":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["user","datafield","file_extension","file_type","file_size","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Teilnehmerdatei ändern","value":{"data":{"file_name":"Unterlagen.pdf"},"id":"file_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"updateFile","line":173},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/file/{id} Teilnehmerdatei löschen Entfernt das Speicherobjekt, leert das zugeordnete Teilnehmer-Dateifeld und markiert die Dateimetadaten als gelöscht. Fehlende oder bereits entfernte aktive Datensätze ergeben delete=false; Speicherfehler werden als Fehler gemeldet. Die Datei wird über die Pfad-ID ausgewählt; ein Request-Body ist für diese Route nicht erforderlich. - Operation-ID: delete_v2_file_id - Dokumentation: /docs/api/v2/operations/delete-v2-file-id-f8b547a20d48/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["file_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/file/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "DELETE", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_file_id","summary":"Teilnehmerdatei löschen","description":"Entfernt das Speicherobjekt, leert das zugeordnete Teilnehmer-Dateifeld und markiert die Dateimetadaten als gelöscht. Fehlende oder bereits entfernte aktive Datensätze ergeben delete=false; Speicherfehler werden als Fehler gemeldet. Die Datei wird über die Pfad-ID ausgewählt; ein Request-Body ist für diese Route nicht erforderlich.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["file_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete geben die angefragte Datei bzw. das Objekt und das Ergebnis an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"file_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","line":190,"sha256":"3e44cb5fcf8fb7f9a57fc5fd11c53501ee655885dd432798fc3f88867a55a81f","method":"deleteFile"},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/file/{id} Teilnehmerdatei lesen Liest Metadaten der aktiven Teilnehmerdateien und deren Zuordnung zu user und datafield. Gelöschte Dateien sind ausgeschlossen; Dateiinhalte und Download-URLs liefert diese Route nicht. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_file_id - Dokumentation: /docs/api/v2/operations/get-v2-file-id-0e36069a36fd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["file_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/file/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_file_id","summary":"Teilnehmerdatei lesen","description":"Liest Metadaten der aktiven Teilnehmerdateien und deren Zuordnung zu user und datafield. Gelöschte Dateien sind ausgeschlossen; Dateiinhalte und Download-URLs liefert diese Route nicht. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["file_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"file_012345abcdef_123456":{"id":"file_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","examples":["file_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"datafield":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":95,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","examples":["file_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"datafield":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":95,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"getFile","line":206},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/file/{id} Teilnehmerdatei ändern Ändert den Anzeigenamen einer Teilnehmerdatei. Die Zuordnung zu user und datafield, MIME-Typ, Erweiterung und gespeicherte Dateigröße bleiben unveränderlich; Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_file_id - Dokumentation: /docs/api/v2/operations/put-v2-file-id-95dbc2c64d1a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["file_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "file_name": "Unterlagen.pdf" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/file/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "file_name": "Unterlagen.pdf" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"file_name\":\"Unterlagen.pdf\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_file_id","summary":"Teilnehmerdatei ändern","description":"Ändert den Anzeigenamen einer Teilnehmerdatei. Die Zuordnung zu user und datafield, MIME-Typ, Erweiterung und gespeicherte Dateigröße bleiben unveränderlich; Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["file_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"file_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"user":false,"datafield":false,"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":63},"file_extension":false,"file_type":false,"file_size":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["user","datafield","file_extension","file_type","file_size","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["file_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Teilnehmerdatei ändern","value":{"data":{"file_name":"Unterlagen.pdf"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"updateFile","line":173},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/file/{id}/url Signierte Download-Adresse einer Teilnehmerdatei beziehen Prüft die aktive Teilnehmerdatei im aktuellen Konto und liefert eine signierte file_url. Jeder erfolgreiche Aufruf schreibt einen Dateizugriff ins Zugriffsprotokoll. Die URL ist vertraulich, zeitlich begrenzt und gehört zur einmaligen Übergabe an den angemeldeten Benutzer. Die Pfad-ID wählt die Datei. - Operation-ID: get_v2_file_id_url - Dokumentation: /docs/api/v2/operations/get-v2-file-id-url-036c8bf134be/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"description":"Öffentliche v2-ID des ausgewählten Objekts."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/file/'"${PATH_ID}"'/url' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file/{id}/url"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file/{id}/url" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_file_id_url","summary":"Signierte Download-Adresse einer Teilnehmerdatei beziehen","description":"Prüft die aktive Teilnehmerdatei im aktuellen Konto und liefert eine signierte file_url. Jeder erfolgreiche Aufruf schreibt einen Dateizugriff ins Zugriffsprotokoll. Die URL ist vertraulich, zeitlich begrenzt und gehört zur einmaligen Übergabe an den angemeldeten Benutzer. Die Pfad-ID wählt die Datei.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"description":"Öffentliche v2-ID des ausgewählten Objekts."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nach öffentlicher file-ID indiziertes data mit id und signierter file_url; kein binärer Dateiinhalt.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"file_url":{"type":"string","format":"uri","description":"Vertrauliche signierte Download-URL.","x-edoobox-sensitive":true}},"additionalProperties":false,"required":["id","file_url"]}},"error":{"const":false},"type":{"type":"string"}},"required":["data","error"],"additionalProperties":true},"example":{"data":{"file_012345abcdef_123456":{"id":"file_012345abcdef_123456","file_url":"https://storage.example.invalid/synthetic-download"}},"error":false,"type":"file"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/File.php","line":219,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged.","sha256":"3e44cb5fcf8fb7f9a57fc5fd11c53501ee655885dd432798fc3f88867a55a81f","method":"getFile"},"x-edoobox-user-handoff":{"sensitivePaths":["/data"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/*/file_url"],"x-edoobox-user-handoff-source":{"file":"application/models/v2/Filefunc.php","line":256,"lineEnd":303,"reason":"The tenant-checked handler returns an ID-keyed data object containing public file IDs and signed storage URLs, then writes the access log. The complete data object is handed to the authenticated human client once; signed URL/signature/legacy storage path never enters model-visible results or result caches. The upstream signed URL itself retains its source-defined lifetime.","sha256":"b0a20df1ad9c325900275984aea76e884aac74f86aa3a08be34da1fed06e87b1","method":"_getFile"},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":338,"reason":"APIPermissions resource mapping to file.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Produces a ten-minute signed read URL for an existing tenant file and INSERTs an access-log record.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/15","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/15","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"getFile","line":206,"branch":{"file":"application/models/v2/File.php","line":219}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/file/list Teilnehmerdatei auflisten Liest Metadaten der aktiven Teilnehmerdateien und deren Zuordnung zu user und datafield. Gelöschte Dateien sind ausgeschlossen; Dateiinhalte und Download-URLs liefert diese Route nicht. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_file_list - Dokumentation: /docs/api/v2/operations/get-v2-file-list-fdbf54a92a5a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/file/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_file_list","summary":"Teilnehmerdatei auflisten","description":"Liest Metadaten der aktiven Teilnehmerdateien und deren Zuordnung zu user und datafield. Gelöschte Dateien sind ausgeschlossen; Dateiinhalte und Download-URLs liefert diese Route nicht. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"file_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"file_012345abcdef_123456":{"id":"file_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","examples":["file_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"datafield":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":95,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","examples":["file_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"datafield":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":95,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"getFile","line":206},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/file/list/url Signierte Download-Adresse einer Teilnehmerdatei beziehen Prüft die aktive Teilnehmerdatei im aktuellen Konto und liefert eine signierte file_url. Jeder erfolgreiche Aufruf schreibt einen Dateizugriff ins Zugriffsprotokoll. Die URL ist vertraulich, zeitlich begrenzt und gehört zur einmaligen Übergabe an den angemeldeten Benutzer. Erforderlich ist der erste filter-Eintrag mit property=id und öffentlicher file-ID. - Operation-ID: get_v2_file_list_url - Dokumentation: /docs/api/v2/operations/get-v2-file-list-url-4534976f4414/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Der erste Eintrag muss property=id enthalten; es wird genau eine aktive Datei ausgewählt. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Der erste Eintrag muss property=id enthalten; es wird genau eine aktive Datei ausgewählt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"file_012345abcdef_123456"}]}}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/file/list/url' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/file/list/url"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/file/list/url" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_file_list_url","summary":"Signierte Download-Adresse einer Teilnehmerdatei beziehen","description":"Prüft die aktive Teilnehmerdatei im aktuellen Konto und liefert eine signierte file_url. Jeder erfolgreiche Aufruf schreibt einen Dateizugriff ins Zugriffsprotokoll. Die URL ist vertraulich, zeitlich begrenzt und gehört zur einmaligen Übergabe an den angemeldeten Benutzer. Erforderlich ist der erste filter-Eintrag mit property=id und öffentlicher file-ID.","tags":["file"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Der erste Eintrag muss property=id enthalten; es wird genau eine aktive Datei ausgewählt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"file_012345abcdef_123456"}]}}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nach öffentlicher file-ID indiziertes data mit id und signierter file_url; kein binärer Dateiinhalt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"file_012345abcdef_123456":{"id":"file_012345abcdef_123456","file_url":"https://storage.example.invalid/synthetic-download"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^file_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["file_012345abcdef_123456"]},"file_url":{"type":"string","format":"uri","description":"Vertrauliche signierte Download-URL.","x-edoobox-sensitive":true}},"additionalProperties":false,"required":["id","file_url"]}},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Filefunc.php","line":256,"sha256":"b0a20df1ad9c325900275984aea76e884aac74f86aa3a08be34da1fed06e87b1","method":"_getFile"},"reason":"A file URL request INSERTs a persistent access-log record."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/File.php","method":"getFile","line":206,"branch":{"file":"application/models/v2/File.php","line":219}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"file","short":"file","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice Rechnungen des Kontos anzeigen Liest Teilnehmerrechnungen mit Betrag, Zahlungssumme, Fälligkeit, Status und gegebenenfalls externer Buchhaltungszuständigkeit. Unterstützt allgemeine Feldselektion, Sortierung und Pagination sowie Filter nach transaction und offer; offer_start_date_filtered bezieht sich auf den offer-Filter. Auch die ID-Selektion liefert standardmäßig eine ID-indizierte Sammlung. fields=["data"] ergänzt den gespeicherten Rechnungsstand. - Operation-ID: get_v2_invoice - Dokumentation: /docs/api/v2/operations/get-v2-invoice-b07d1255f372/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice","summary":"Rechnungen des Kontos anzeigen","description":"Liest Teilnehmerrechnungen mit Betrag, Zahlungssumme, Fälligkeit, Status und gegebenenfalls externer Buchhaltungszuständigkeit. Unterstützt allgemeine Feldselektion, Sortierung und Pagination sowie Filter nach transaction und offer; offer_start_date_filtered bezieht sich auf den offer-Filter. Auch die ID-Selektion liefert standardmäßig eine ID-indizierte Sammlung. fields=[\"data\"] ergänzt den gespeicherten Rechnungsstand.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Rechnungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"invoice_012345abcdef_123456":{"id":"invoice_012345abcdef_123456","amount":120,"amount_pay":40,"currency":"CHF","number":"INV-EXAMPLE","description":"Beispielkurs","user":"user_012345abcdef_123456","paytrans":"paytrans_012345abcdef_123456","date_create":"2026-10-15T10:00:00+02:00","date_payuntil":"2026-11-15T00:00:00+01:00","date_pay":false,"date_cancelled":false,"status":1}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"date_payuntil":{"anyOf":[{"type":"string"},{"const":false}]},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"date_cancelled":{"anyOf":[{"type":"string"},{"const":false}]},"status":{"type":"integer"},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]},"offer_start_date_filtered":{"anyOf":[{"type":"string"},{"const":false}]},"data":{"description":"Optionales gespeichertes Rechnungsdatenobjekt bei fields=[\"data\"]; Inhalt entspricht dem damaligen Rechnungsstand.","type":["object","array","null"]},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"date_payuntil":{"anyOf":[{"type":"string"},{"const":false}]},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"date_cancelled":{"anyOf":[{"type":"string"},{"const":false}]},"status":{"type":"integer"},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]},"offer_start_date_filtered":{"anyOf":[{"type":"string"},{"const":false}]},"data":{"description":"Optionales gespeichertes Rechnungsdatenobjekt bei fields=[\"data\"]; Inhalt entspricht dem damaligen Rechnungsstand.","type":["object","array","null"]},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/invoice Rechnung für abrechenbare Positionen erstellen Erstellt eine Teilnehmerrechnung aus einer nichtleeren, duplikatfreien Liste öffentlicher transaction-IDs. Alle Positionen müssen zum selben Teilnehmer und zur selben Währung gehören und aktuell abrechenbar sein; die Prüfung erfolgt unter Sperren. Kann eine bereits identisch erzeugte Rechnung mit idempotent_reuse zurückgeben. Buchhaltungs- oder Mitgliedschaftsbedingungen bleiben maßgeblich. - Operation-ID: post_v2_invoice - Dokumentation: /docs/api/v2/operations/post-v2-invoice-cc6a4b5ea7b8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "transaction": [ "transaction_012345abcdef_123456" ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "transaction": [ "transaction_012345abcdef_123456" ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"transaction\":[\"transaction_012345abcdef_123456\"]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_invoice","summary":"Rechnung für abrechenbare Positionen erstellen","description":"Erstellt eine Teilnehmerrechnung aus einer nichtleeren, duplikatfreien Liste öffentlicher transaction-IDs. Alle Positionen müssen zum selben Teilnehmer und zur selben Währung gehören und aktuell abrechenbar sein; die Prüfung erfolgt unter Sperren. Kann eine bereits identisch erzeugte Rechnung mit idempotent_reuse zurückgeben. Buchhaltungs- oder Mitgliedschaftsbedingungen bleiben maßgeblich.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"insert=true, öffentliche Rechnungs-ID und paytrans-ID; idempotent_reuse kann eine vorhandene Rechnung kennzeichnen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"invoice_012345abcdef_123456","paytrans":"paytrans_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"idempotent_reuse":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"transaction":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]}}},"additionalProperties":true,"required":["transaction"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Rechnung für abrechenbare Positionen erstellen","value":{"data":{"transaction":["transaction_012345abcdef_123456"]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"postInvoice","line":229},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/invoice Fälligkeit einer Rechnung ändern Aktualisiert das beschreibbare Feld date_payuntil einer eigenen Rechnung. Betrag, Währung, Teilnehmer, Rechnungsnummer und Zahlungsstatus werden über diesen allgemeinen Schreibweg nicht geändert; für Zahlung, Erstattung, Versand und Stornierung die benannten Aktionen verwenden. Beim Basisendpunkt ist äußeres id erforderlich. - Operation-ID: put_v2_invoice - Dokumentation: /docs/api/v2/operations/put-v2-invoice-37f38c23b19e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "date_payuntil": "2026-11-15T00:00:00+01:00" }, "id": "invoice_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "date_payuntil": "2026-11-15T00:00:00+01:00" }, "id": "invoice_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"date_payuntil\":\"2026-11-15T00:00:00+01:00\"},\"id\":\"invoice_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_invoice","summary":"Fälligkeit einer Rechnung ändern","description":"Aktualisiert das beschreibbare Feld date_payuntil einer eigenen Rechnung. Betrag, Währung, Teilnehmer, Rechnungsnummer und Zahlungsstatus werden über diesen allgemeinen Schreibweg nicht geändert; für Zahlung, Erstattung, Versand und Stornierung die benannten Aktionen verwenden. Beim Basisendpunkt ist äußeres id erforderlich.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update und öffentliche Rechnungs-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"invoice_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"amount":false,"amount_pay":false,"currency":false,"number":false,"description":false,"user":false,"paytrans":false,"date_create":false,"date_payuntil":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":127},"date_pay":false,"date_cancelled":false,"status":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["amount","amount_pay","currency","number","description","user","paytrans","date_create","date_pay","date_cancelled","status"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Fälligkeit einer Rechnung ändern","value":{"data":{"date_payuntil":"2026-11-15T00:00:00+01:00"},"id":"invoice_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"updateInvoice","line":245},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id} Rechnung des Kontos prüfen Liest Teilnehmerrechnungen mit Betrag, Zahlungssumme, Fälligkeit, Status und gegebenenfalls externer Buchhaltungszuständigkeit. Unterstützt allgemeine Feldselektion, Sortierung und Pagination sowie Filter nach transaction und offer; offer_start_date_filtered bezieht sich auf den offer-Filter. Auch die ID-Selektion liefert standardmäßig eine ID-indizierte Sammlung. fields=["data"] ergänzt den gespeicherten Rechnungsstand. - Operation-ID: get_v2_invoice_id - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-2412017470c4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id","summary":"Rechnung des Kontos prüfen","description":"Liest Teilnehmerrechnungen mit Betrag, Zahlungssumme, Fälligkeit, Status und gegebenenfalls externer Buchhaltungszuständigkeit. Unterstützt allgemeine Feldselektion, Sortierung und Pagination sowie Filter nach transaction und offer; offer_start_date_filtered bezieht sich auf den offer-Filter. Auch die ID-Selektion liefert standardmäßig eine ID-indizierte Sammlung. fields=[\"data\"] ergänzt den gespeicherten Rechnungsstand.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Rechnungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"invoice_012345abcdef_123456":{"id":"invoice_012345abcdef_123456","amount":120,"amount_pay":40,"currency":"CHF","number":"INV-EXAMPLE","description":"Beispielkurs","user":"user_012345abcdef_123456","paytrans":"paytrans_012345abcdef_123456","date_create":"2026-10-15T10:00:00+02:00","date_payuntil":"2026-11-15T00:00:00+01:00","date_pay":false,"date_cancelled":false,"status":1}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"date_payuntil":{"anyOf":[{"type":"string"},{"const":false}]},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"date_cancelled":{"anyOf":[{"type":"string"},{"const":false}]},"status":{"type":"integer"},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]},"offer_start_date_filtered":{"anyOf":[{"type":"string"},{"const":false}]},"data":{"description":"Optionales gespeichertes Rechnungsdatenobjekt bei fields=[\"data\"]; Inhalt entspricht dem damaligen Rechnungsstand.","type":["object","array","null"]},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"date_payuntil":{"anyOf":[{"type":"string"},{"const":false}]},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"date_cancelled":{"anyOf":[{"type":"string"},{"const":false}]},"status":{"type":"integer"},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]},"offer_start_date_filtered":{"anyOf":[{"type":"string"},{"const":false}]},"data":{"description":"Optionales gespeichertes Rechnungsdatenobjekt bei fields=[\"data\"]; Inhalt entspricht dem damaligen Rechnungsstand.","type":["object","array","null"]},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/invoice/{id} Fälligkeit einer Rechnung ändern Aktualisiert das beschreibbare Feld date_payuntil einer eigenen Rechnung. Betrag, Währung, Teilnehmer, Rechnungsnummer und Zahlungsstatus werden über diesen allgemeinen Schreibweg nicht geändert; für Zahlung, Erstattung, Versand und Stornierung die benannten Aktionen verwenden. Beim Basisendpunkt ist äußeres id erforderlich. - Operation-ID: put_v2_invoice_id - Dokumentation: /docs/api/v2/operations/put-v2-invoice-id-2d0f382f775d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "date_payuntil": "2026-11-15T00:00:00+01:00" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "date_payuntil": "2026-11-15T00:00:00+01:00" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"date_payuntil\":\"2026-11-15T00:00:00+01:00\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_invoice_id","summary":"Fälligkeit einer Rechnung ändern","description":"Aktualisiert das beschreibbare Feld date_payuntil einer eigenen Rechnung. Betrag, Währung, Teilnehmer, Rechnungsnummer und Zahlungsstatus werden über diesen allgemeinen Schreibweg nicht geändert; für Zahlung, Erstattung, Versand und Stornierung die benannten Aktionen verwenden. Beim Basisendpunkt ist äußeres id erforderlich.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update und öffentliche Rechnungs-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"invoice_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"amount":false,"amount_pay":false,"currency":false,"number":false,"description":false,"user":false,"paytrans":false,"date_create":false,"date_payuntil":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":127},"date_pay":false,"date_cancelled":false,"status":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["amount","amount_pay","currency","number","description","user","paytrans","date_create","date_pay","date_cancelled","status"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Fälligkeit einer Rechnung ändern","value":{"data":{"date_payuntil":"2026-11-15T00:00:00+01:00"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"updateInvoice","line":245},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/invoice/{id}/cancel Rechnung stornieren Storniert eine offene eigene Rechnung einschließlich der zugehörigen Zahlungs-/Belegverarbeitung. Bereits stornierte Rechnungen liefern update=false. Externe Buchhaltungszuständigkeit kann die lokale Aktion sperren oder pending=true mit Grund melden; pending ist kein abgeschlossener Vorgang. - Operation-ID: put_v2_invoice_id_cancel - Dokumentation: /docs/api/v2/operations/put-v2-invoice-id-cancel-3c45a413da1c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/cancel' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/cancel"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/cancel" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_invoice_id_cancel","summary":"Rechnung stornieren","description":"Storniert eine offene eigene Rechnung einschließlich der zugehörigen Zahlungs-/Belegverarbeitung. Bereits stornierte Rechnungen liefern update=false. Externe Buchhaltungszuständigkeit kann die lokale Aktion sperren oder pending=true mit Grund melden; pending ist kein abgeschlossener Vorgang.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update und öffentliche ID; bei blockierter externer Stornierung zusätzlich pending/reason.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"invoice_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"pending":{"type":"boolean"},"reason":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Invoice.php","line":367},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Rechnung stornieren","value":{}}}}},"required":false},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"updateInvoice","line":245,"branch":{"file":"application/models/v2/Invoice.php","line":367}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/dashboard Rechnungsübersicht zusammenstellen Stellt eine vollständige Rechnungsansicht aus Rechnung, Teilnehmerdaten, Belegen, HTML-Positionenvorlage und zugehörigen Transaktionen zusammen. refresh_suggested vergleicht den gespeicherten Rechnungsstand mit den aktuellen Positionen. Führt selbst keine Neuberechnung oder Zahlung aus. - Operation-ID: get_v2_invoice_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-dashboard-018caad6cccd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_dashboard","summary":"Rechnungsübersicht zusammenstellen","description":"Stellt eine vollständige Rechnungsansicht aus Rechnung, Teilnehmerdaten, Belegen, HTML-Positionenvorlage und zugehörigen Transaktionen zusammen. refresh_suggested vergleicht den gespeicherten Rechnungsstand mit den aktuellen Positionen. Führt selbst keine Neuberechnung oder Zahlung aus.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Zusammengesetztes Dashboardobjekt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"invoice":{"id":"invoice_012345abcdef_123456","amount":120,"amount_pay":40,"currency":"CHF","number":"INV-EXAMPLE","description":"Beispielkurs","user":"user_012345abcdef_123456","paytrans":"paytrans_012345abcdef_123456","date_create":"2026-10-15T10:00:00+02:00","date_payuntil":"2026-11-15T00:00:00+01:00","date_pay":false,"date_cancelled":false,"status":1},"invoice_data":{},"user":{"id":"user_012345abcdef_123456"},"receipts":[],"template":"
","transactions":[],"refresh_suggested":false,"invoice_id":"invoice_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"invoice":{"type":"object","properties":{},"additionalProperties":true},"invoice_data":{"type":"object","properties":{},"additionalProperties":true},"user":{"type":"object","properties":{},"additionalProperties":true},"receipts":{"type":["object","array"]},"template":{"type":"string"},"transactions":{"type":["object","array"]},"refresh_suggested":{"type":"boolean"},"invoice_id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/dashboard/Invoicedashboard.php","line":9},"reason":"Complete reachable graph reviewed: existing invoice/user/transaction/receipt snapshots, SELECT payment/refund allocations and authority metadata, pure formatting/template output. Does not invoke invoice refresh, payment persistence, PDF storage or provider token refresh. Derived read caches/logging are not domain mutations.","relatedSources":[{"file":"application/models/v2/Invoice.php","line":1110},{"file":"application/models/v2/Transaction.php","line":443}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":724}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/data Rechnungsdetails und Zahlungspositionen anzeigen Liefert die gewählte Rechnung mit aktuellem bezahltem und manuell erstattbarem Betrag, Belegen, gespeicherten Rechnungsdaten, Zahlungen/Erstattungen und aufbereiteten Positionen. payment bezeichnet die ursprüngliche öffentliche pay-ID; Erstattungszeilen können eine refund-ID besitzen. Buchhaltungsmetadaten kennzeichnen externe Zuständigkeit. - Operation-ID: get_v2_invoice_id_data - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-data-50d358d39d8c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/data' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/data"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/data" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_data","summary":"Rechnungsdetails und Zahlungspositionen anzeigen","description":"Liefert die gewählte Rechnung mit aktuellem bezahltem und manuell erstattbarem Betrag, Belegen, gespeicherten Rechnungsdaten, Zahlungen/Erstattungen und aufbereiteten Positionen. payment bezeichnet die ursprüngliche öffentliche pay-ID; Erstattungszeilen können eine refund-ID besitzen. Buchhaltungsmetadaten kennzeichnen externe Zuständigkeit.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Rechnungssammlung mit erweiterten Detailfeldern und limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"invoice_012345abcdef_123456":{"id":"invoice_012345abcdef_123456","amount":120,"amount_pay":40,"currency":"CHF","number":"INV-EXAMPLE","description":"Beispielkurs","user":"user_012345abcdef_123456","paytrans":"paytrans_012345abcdef_123456","date_create":"2026-10-15T10:00:00+02:00","date_payuntil":"2026-11-15T00:00:00+01:00","date_pay":false,"date_cancelled":false,"status":1,"amount_refundable_manual":40,"receipt":[],"invoice_data":null,"pay":[],"items":{"contained":[],"amount":120,"vat_included":true}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"date_payuntil":{"anyOf":[{"type":"string"},{"const":false}]},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"date_cancelled":{"anyOf":[{"type":"string"},{"const":false}]},"status":{"type":"integer"},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]},"offer_start_date_filtered":{"anyOf":[{"type":"string"},{"const":false}]},"data":{"description":"Optionales gespeichertes Rechnungsdatenobjekt bei fields=[\"data\"]; Inhalt entspricht dem damaligen Rechnungsstand.","type":["object","array","null"]},"accounting":{"type":"object","properties":{},"additionalProperties":true},"amount_refundable_manual":{"type":"number"},"receipt":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"invoice":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string","enum":["receipt","cancel_receipt"]},"data":{"type":["object","array","null"]},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"invoice":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string","enum":["receipt","cancel_receipt"]},"data":{"type":["object","array","null"]},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"invoice_data":{"type":["object","array","null"]},"pay":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"status":{"type":"integer"},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"system":{"anyOf":[{"type":"string"},{"const":false}]},"system_id":{"type":"string"},"payment":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},"is_refund":{"type":"boolean"},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"status":{"type":"integer"},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"system":{"anyOf":[{"type":"string"},{"const":false}]},"system_id":{"type":"string"},"payment":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},"is_refund":{"type":"boolean"},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"items":{"type":"object","properties":{"contained":{"type":["array","object"],"items":{"type":"object","properties":{},"additionalProperties":true},"additionalProperties":{"type":"object","properties":{},"additionalProperties":true},"description":"Rechnungspositionen (t_booking/t_manualtrans/t_membership) mit Beträgen, MwSt und je nach Typ öffentlichen Verknüpfungen."},"amount":{"type":"number"},"vat_included":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"date_payuntil":{"anyOf":[{"type":"string"},{"const":false}]},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"date_cancelled":{"anyOf":[{"type":"string"},{"const":false}]},"status":{"type":"integer"},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]},"offer_start_date_filtered":{"anyOf":[{"type":"string"},{"const":false}]},"data":{"description":"Optionales gespeichertes Rechnungsdatenobjekt bei fields=[\"data\"]; Inhalt entspricht dem damaligen Rechnungsstand.","type":["object","array","null"]},"accounting":{"type":"object","properties":{},"additionalProperties":true},"amount_refundable_manual":{"type":"number"},"receipt":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"invoice":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string","enum":["receipt","cancel_receipt"]},"data":{"type":["object","array","null"]},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"invoice":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string","enum":["receipt","cancel_receipt"]},"data":{"type":["object","array","null"]},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"invoice_data":{"type":["object","array","null"]},"pay":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"status":{"type":"integer"},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"system":{"anyOf":[{"type":"string"},{"const":false}]},"system_id":{"type":"string"},"payment":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},"is_refund":{"type":"boolean"},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"status":{"type":"integer"},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"system":{"anyOf":[{"type":"string"},{"const":false}]},"system_id":{"type":"string"},"payment":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},"is_refund":{"type":"boolean"},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"items":{"type":"object","properties":{"contained":{"type":["array","object"],"items":{"type":"object","properties":{},"additionalProperties":true},"additionalProperties":{"type":"object","properties":{},"additionalProperties":true},"description":"Rechnungspositionen (t_booking/t_manualtrans/t_membership) mit Beträgen, MwSt und je nach Typ öffentlichen Verknüpfungen."},"amount":{"type":"number"},"vat_included":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/18","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":258,"reason":"APIPermissions resource mapping to invo.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Reads invoice-linked transaction/payment snapshots and formats a structured data response.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/18","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":735}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/invoice/{id}/deposit Zahlungseingang auf Rechnung verbuchen Verbucht einen Zahlungseingang auf die Rechnung, aktualisiert Zahlungsstand, zugehörige Buchungen, Rechnung und Webhook. amount ist erforderlich und darf nicht negativ sein; eine stornierte Rechnung liefert update=false mit reason.canceled. Dies dokumentiert einen Zahlungseingang und löst keine Kartenbelastung aus. Ohne gültiges date_accounting wird die aktuelle Serverzeit verwendet. - Operation-ID: put_v2_invoice_id_deposit - Dokumentation: /docs/api/v2/operations/put-v2-invoice-id-deposit-1f8b21010a04/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "amount": 40, "date_accounting": "2026-10-15T10:00:00+02:00", "provider_transaction_id": "PAYMENT-EXAMPLE" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/deposit' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/deposit"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "amount": 40, "date_accounting": "2026-10-15T10:00:00+02:00", "provider_transaction_id": "PAYMENT-EXAMPLE" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/deposit" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"amount\":40,\"date_accounting\":\"2026-10-15T10:00:00+02:00\",\"provider_transaction_id\":\"PAYMENT-EXAMPLE\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_invoice_id_deposit","summary":"Zahlungseingang auf Rechnung verbuchen","description":"Verbucht einen Zahlungseingang auf die Rechnung, aktualisiert Zahlungsstand, zugehörige Buchungen, Rechnung und Webhook. amount ist erforderlich und darf nicht negativ sein; eine stornierte Rechnung liefert update=false mit reason.canceled. Dies dokumentiert einen Zahlungseingang und löst keine Kartenbelastung aus. Ohne gültiges date_accounting wird die aktuelle Serverzeit verwendet.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update und Rechnungs-ID; bei stornierter Rechnung zusätzlich Status und Grund.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"invoice_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"update":{"type":"boolean"},"status":{"type":"integer"},"reason":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"amount":{"anyOf":[{"type":"number"},{"type":"string","description":"Numerische Zeichenfolge; PHP is_numeric beziehungsweise die dokumentierte Zahlen-Normalisierung ist maßgeblich."}],"description":"Betrag in Rechnungswährung; numerische Zeichenfolgen werden akzeptiert und auf zwei Stellen gerundet."},"date_accounting":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"provider_transaction_id":{"type":"string","description":"Optionaler Zahlungsbezug; Standard ist die Rechnungsnummer."},"accounting_origin":{"type":"string","description":"Optionaler Herkunftshinweis für die Buchhaltungsintegration; Standard invoice."}},"additionalProperties":true,"required":["amount"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zahlungseingang auf Rechnung verbuchen","value":{"data":{"amount":40,"date_accounting":"2026-10-15T10:00:00+02:00","provider_transaction_id":"PAYMENT-EXAMPLE"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"updateInvoice","line":245,"branch":{"file":"application/models/v2/Invoice.php","line":312}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/invoice.pdf Rechnungsdokument herunterladen Erzeugt die gewählte Teilnehmerrechnung als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Extern geführte Rechnungen sind gesperrt und liefern external_invoice_authority. - Operation-ID: get_v2_invoice_id_invoice_pdf - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-invoice-pdf-9130e36c9d26/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | pdf | query | nein | Darstellung des erzeugten Dokuments; andere Werte verwenden download. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche invoice-ID des zu ladenden Dokuments. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"description":"Öffentliche invoice-ID des zu ladenden Dokuments."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/invoice.pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/invoice.pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/invoice.pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_invoice_pdf","summary":"Rechnungsdokument herunterladen","description":"Erzeugt die gewählte Teilnehmerrechnung als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Extern geführte Rechnungen sind gesperrt und liefern external_invoice_authority.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"description":"Öffentliche invoice-ID des zu ladenden Dokuments."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"PDF- oder XRechnung-XML-Dokument; Fehler verwenden die JSON-Fehlerhülle.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}},"application/xml":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Invoice.php","line":692,"reason":"The dispatcher converts the third segment into the ID filter; this PDF branch explicitly decodes it as invoice. Vue appends only the reviewed cAe timestamp cache segment."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":692}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/invoice.pdf/{cacheToken} Rechnungsdokument herunterladen Erzeugt die gewählte Teilnehmerrechnung als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Extern geführte Rechnungen sind gesperrt und liefern external_invoice_authority. cacheToken ist der zuvor ausgestellte, kontogebundene Download-Token; die bestehende Tokenautorisierung gilt weiterhin. - Operation-ID: get_v2_invoice_id_invoice_pdf_cacheToken - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-invoice-pdf-cachetoken-79f561eabf05/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | pdf | query | nein | Darstellung des erzeugten Dokuments; andere Werte verwenden download. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche invoice-ID des zu ladenden Dokuments. | | cacheToken | path | ja | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"description":"Öffentliche invoice-ID des zu ladenden Dokuments."},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${PATH_CACHETOKEN:?PATH_CACHETOKEN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/invoice.pdf/'"${PATH_CACHETOKEN}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/invoice.pdf/{cacheToken}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); path = path.replaceAll("{cacheToken}", encodeURIComponent(required("PATH_CACHETOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/invoice.pdf/{cacheToken}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) path = path.replace("{cacheToken}", urllib.parse.quote(os.environ["PATH_CACHETOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_invoice_pdf_cacheToken","summary":"Rechnungsdokument herunterladen","description":"Erzeugt die gewählte Teilnehmerrechnung als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Extern geführte Rechnungen sind gesperrt und liefern external_invoice_authority. cacheToken ist der zuvor ausgestellte, kontogebundene Download-Token; die bestehende Tokenautorisierung gilt weiterhin.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"description":"Öffentliche invoice-ID des zu ladenden Dokuments."},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}],"responses":{"200":{"description":"PDF- oder XRechnung-XML-Dokument; Fehler verwenden die JSON-Fehlerhülle.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}},"application/xml":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Invoice.php","line":692,"reason":"The dispatcher converts the third segment into the ID filter; this PDF branch explicitly decodes it as invoice. Vue appends only the reviewed cAe timestamp cache segment."},"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/19","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":258,"reason":"APIPermissions resource mapping to invo.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Downloads/renders an already stored invoice snapshot. The optional background PDF cache is derived data; invoice/payment state is unchanged.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/19","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":692}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/receipt/invoicereceipt.pdf Rechnungsbeleg herunterladen Erzeugt den gewählten Rechnungsbeleg als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Die ID bezeichnet einen invoicereceipt-Beleg. - Operation-ID: get_v2_invoice_id_receipt_invoicereceipt_pdf - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-receipt-invoicereceipt-pdf-e06e6c125022/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | pdf | query | nein | Darstellung des erzeugten Dokuments; andere Werte verwenden download. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche invoicereceipt-ID des zu ladenden Dokuments. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"description":"Öffentliche invoicereceipt-ID des zu ladenden Dokuments."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/receipt/invoicereceipt.pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/receipt/invoicereceipt.pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/receipt/invoicereceipt.pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_receipt_invoicereceipt_pdf","summary":"Rechnungsbeleg herunterladen","description":"Erzeugt den gewählten Rechnungsbeleg als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Die ID bezeichnet einen invoicereceipt-Beleg.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"description":"Öffentliche invoicereceipt-ID des zu ladenden Dokuments."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"PDF- oder XRechnung-XML-Dokument; Fehler verwenden die JSON-Fehlerhülle.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}},"application/xml":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Invoice.php","line":675,"reason":"The dispatcher converts the third segment into the ID filter; this PDF branch explicitly decodes it as invoicereceipt. Vue appends only the reviewed cAe timestamp cache segment."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":675}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/receipt/invoicereceipt.pdf/{cacheToken} Rechnungsbeleg herunterladen Erzeugt den gewählten Rechnungsbeleg als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Die ID bezeichnet einen invoicereceipt-Beleg. cacheToken ist der zuvor ausgestellte, kontogebundene Download-Token; die bestehende Tokenautorisierung gilt weiterhin. - Operation-ID: get_v2_invoice_id_receipt_invoicereceipt_pdf_cacheToken - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-receipt-invoicereceipt-pdf-cachetoken-5d75f01a8e87/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | pdf | query | nein | Darstellung des erzeugten Dokuments; andere Werte verwenden download. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche invoicereceipt-ID des zu ladenden Dokuments. | | cacheToken | path | ja | | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"description":"Öffentliche invoicereceipt-ID des zu ladenden Dokuments."},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${PATH_CACHETOKEN:?PATH_CACHETOKEN setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/receipt/invoicereceipt.pdf/'"${PATH_CACHETOKEN}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/receipt/invoicereceipt.pdf/{cacheToken}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); path = path.replaceAll("{cacheToken}", encodeURIComponent(required("PATH_CACHETOKEN"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/receipt/invoicereceipt.pdf/{cacheToken}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) path = path.replace("{cacheToken}", urllib.parse.quote(os.environ["PATH_CACHETOKEN"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_receipt_invoicereceipt_pdf_cacheToken","summary":"Rechnungsbeleg herunterladen","description":"Erzeugt den gewählten Rechnungsbeleg als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Die ID bezeichnet einen invoicereceipt-Beleg. cacheToken ist der zuvor ausgestellte, kontogebundene Download-Token; die bestehende Tokenautorisierung gilt weiterhin.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"description":"Öffentliche invoicereceipt-ID des zu ladenden Dokuments."},{"name":"cacheToken","in":"path","required":true,"schema":{"type":"string","pattern":"^cAe-[0-9]+$","description":"Vue cache-busting token. The action consumes its ID/target before this exact trailing cache segment.","examples":["cAe-1792137600000"]}}],"responses":{"200":{"description":"PDF- oder XRechnung-XML-Dokument; Fehler verwenden die JSON-Fehlerhülle.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}},"application/xml":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Invoice.php","line":675,"reason":"The dispatcher converts the third segment into the ID filter; this PDF branch explicitly decodes it as invoicereceipt. Vue appends only the reviewed cAe timestamp cache segment."},"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/20","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":258,"reason":"APIPermissions resource mapping to invo.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Downloads/renders an already stored receipt snapshot. The optional background PDF cache is derived data; invoice/payment state is unchanged.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/20","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":675}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/invoice/{id}/refresh Rechnung aus Positionen neu berechnen Aktualisiert die Rechnung aus den aktuellen Positionen, gleicht zurückbehaltene Erstattungsgebühren ab und erzeugt einen Rechnungs-Webhook. Externe Buchhaltungszuständigkeit kann die lokale Aktualisierung sperren. - Operation-ID: put_v2_invoice_id_refresh - Dokumentation: /docs/api/v2/operations/put-v2-invoice-id-refresh-237ec2a53c7f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/refresh' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/refresh"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/refresh" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_invoice_id_refresh","summary":"Rechnung aus Positionen neu berechnen","description":"Aktualisiert die Rechnung aus den aktuellen Positionen, gleicht zurückbehaltene Erstattungsgebühren ab und erzeugt einen Rechnungs-Webhook. Externe Buchhaltungszuständigkeit kann die lokale Aktualisierung sperren.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update und öffentliche ID; bei blockierter externer Stornierung zusätzlich pending/reason.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"invoice_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"pending":{"type":"boolean"},"reason":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Rechnung aus Positionen neu berechnen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"updateInvoice","line":245,"branch":{"file":"application/models/v2/Invoice.php","line":348}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/invoice/{id}/refresh_payments Rechnungszahlungen und Dokument abgleichen Berechnet Zahlungsstand und Rechnungsdokument aus den verbuchten Zahlungen/Erstattungen neu. Erstellt einen Rechnungs-Webhook, wenn der Zahlungsstand aktualisiert wurde. pdf_updated zeigt getrennt, ob auch das Dokument aktualisiert wurde; bei einem Dokumentfehler die ursprüngliche Zahlung nicht wiederholen. - Operation-ID: put_v2_invoice_id_refresh_payments - Dokumentation: /docs/api/v2/operations/put-v2-invoice-id-refresh-payments-81fc1b97f1b7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/refresh_payments' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/refresh_payments"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/refresh_payments" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_invoice_id_refresh_payments","summary":"Rechnungszahlungen und Dokument abgleichen","description":"Berechnet Zahlungsstand und Rechnungsdokument aus den verbuchten Zahlungen/Erstattungen neu. Erstellt einen Rechnungs-Webhook, wenn der Zahlungsstand aktualisiert wurde. pdf_updated zeigt getrennt, ob auch das Dokument aktualisiert wurde; bei einem Dokumentfehler die ursprüngliche Zahlung nicht wiederholen.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche ID mit update und pdf_updated.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"invoice_012345abcdef_123456","update":true,"pdf_updated":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"update":{"type":"boolean"},"pdf_updated":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Rechnungszahlungen und Dokument abgleichen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"updateInvoice","line":245,"branch":{"file":"application/models/v2/Invoice.php","line":338}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/refresh_suggested Änderungsbedarf einer Rechnung prüfen Vergleicht den gespeicherten Rechnungsbetrag mit den aktuellen zugehörigen Transaktionen einschließlich unverknüpfter Positionen. Liefert nur einen Hinweis, ohne die Rechnung neu zu berechnen. Für nicht aktualisierbare beziehungsweise stornierte Rechnungen wird kein Vorschlag erteilt. - Operation-ID: get_v2_invoice_id_refresh_suggested - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-refresh-suggested-7bec6499b5f0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/refresh_suggested' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/refresh_suggested"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/refresh_suggested" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_refresh_suggested","summary":"Änderungsbedarf einer Rechnung prüfen","description":"Vergleicht den gespeicherten Rechnungsbetrag mit den aktuellen zugehörigen Transaktionen einschließlich unverknüpfter Positionen. Liefert nur einen Hinweis, ohne die Rechnung neu zu berechnen. Für nicht aktualisierbare beziehungsweise stornierte Rechnungen wird kein Vorschlag erteilt.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Rechnungs-ID und refresh_suggested.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"invoice_012345abcdef_123456","refresh_suggested":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"refresh_suggested":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Invoicefunc.php","line":2638},"reason":"Refresh-suggested compares data only; XRechnung export uses a temporary ZIP deleted after streaming."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":727}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/invoice/{id}/refund Manuelle Rechnungserstattung verbuchen Verbucht eine manuelle Erstattung gegen bereits manuell erstattbare Rechnungszahlungen. amount muss nach Rundung positiv sein und darf den manuell erstattbaren Restbetrag nicht überschreiten. Erzeugt einen negativen Zahlungseintrag und aktualisiert Rechnung, Belege und Webhook. Es findet keine automatische Auszahlung beim Zahlungsanbieter statt; externe Buchhaltungszuständigkeit kann die Aktion sperren. Ohne gültiges date_accounting wird die aktuelle Serverzeit verwendet. - Operation-ID: put_v2_invoice_id_refund - Dokumentation: /docs/api/v2/operations/put-v2-invoice-id-refund-8ba090828509/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "amount": 20, "date_accounting": "2026-10-15T10:00:00+02:00", "provider_transaction_id": "PAYMENT-EXAMPLE" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/refund' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/refund"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "amount": 20, "date_accounting": "2026-10-15T10:00:00+02:00", "provider_transaction_id": "PAYMENT-EXAMPLE" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/refund" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"amount\":20,\"date_accounting\":\"2026-10-15T10:00:00+02:00\",\"provider_transaction_id\":\"PAYMENT-EXAMPLE\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_invoice_id_refund","summary":"Manuelle Rechnungserstattung verbuchen","description":"Verbucht eine manuelle Erstattung gegen bereits manuell erstattbare Rechnungszahlungen. amount muss nach Rundung positiv sein und darf den manuell erstattbaren Restbetrag nicht überschreiten. Erzeugt einen negativen Zahlungseintrag und aktualisiert Rechnung, Belege und Webhook. Es findet keine automatische Auszahlung beim Zahlungsanbieter statt; externe Buchhaltungszuständigkeit kann die Aktion sperren. Ohne gültiges date_accounting wird die aktuelle Serverzeit verwendet.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update und Rechnungs-ID; bei stornierter Rechnung zusätzlich Status und Grund.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"invoice_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"update":{"type":"boolean"},"status":{"type":"integer"},"reason":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"amount":{"anyOf":[{"type":"number"},{"type":"string","description":"Numerische Zeichenfolge; PHP is_numeric beziehungsweise die dokumentierte Zahlen-Normalisierung ist maßgeblich."}],"description":"Betrag in Rechnungswährung; numerische Zeichenfolgen werden akzeptiert und auf zwei Stellen gerundet."},"date_accounting":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"provider_transaction_id":{"type":"string","description":"Optionaler Zahlungsbezug; Standard ist die Rechnungsnummer."},"accounting_origin":{"type":"string","description":"Optionaler Herkunftshinweis für die Buchhaltungsintegration; Standard invoice."}},"additionalProperties":true,"required":["amount"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Manuelle Rechnungserstattung verbuchen","value":{"data":{"amount":20,"date_accounting":"2026-10-15T10:00:00+02:00","provider_transaction_id":"PAYMENT-EXAMPLE"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"updateInvoice","line":245,"branch":{"file":"application/models/v2/Invoice.php","line":325}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/invoice/{id}/send Rechnung versenden Versendet die Rechnung anhand der eingerichteten Empfänger und Vorlagen per E-Mail oder Brief. data.send_type ist erforderlich: email oder letter. Externe Buchhaltungszuständigkeit kann lokalen Versand sperren. data.send.send und message geben das eigentliche Versandergebnis wieder; update bleibt hier false. - Operation-ID: put_v2_invoice_id_send - Dokumentation: /docs/api/v2/operations/put-v2-invoice-id-send-8dfe433f4a67/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "send_type": "email" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/send' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/send"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "send_type": "email" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/send" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"send_type\":\"email\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_invoice_id_send","summary":"Rechnung versenden","description":"Versendet die Rechnung anhand der eingerichteten Empfänger und Vorlagen per E-Mail oder Brief. data.send_type ist erforderlich: email oder letter. Externe Buchhaltungszuständigkeit kann lokalen Versand sperren. data.send.send und message geben das eigentliche Versandergebnis wieder; update bleibt hier false.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Objekt mit Versandergebnis send, update=false und Rechnungs-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"send":{"send":true,"message":"Beispiel: Versand gestartet"},"update":false,"id":"invoice_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"send":{"type":"object","properties":{"send":{"type":"boolean"},"message":{"type":"string"}},"additionalProperties":true},"update":{"const":false},"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"send_type":{"type":"string","enum":["email","letter"]}},"additionalProperties":true,"required":["send_type"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Rechnung versenden","value":{"data":{"send_type":"email"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"updateInvoice","line":245,"branch":{"file":"application/models/v2/Invoice.php","line":402}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/transaction_template Rechnungspositionen als HTML-Vorlage laden Liefert das gerenderte HTML der Rechnungspositionen zur Vorschau, einschließlich Teilnehmer- und Preiskategorieanzeige. id stammt aus dem Pfad; der HTTP-API-Aufruf verpackt template und id in JSON. - Operation-ID: get_v2_invoice_id_transaction_template - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-transaction-template-c6bf7317fa9b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/transaction_template' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/transaction_template"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/transaction_template" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_transaction_template","summary":"Rechnungspositionen als HTML-Vorlage laden","description":"Liefert das gerenderte HTML der Rechnungspositionen zur Vorschau, einschließlich Teilnehmer- und Preiskategorieanzeige. id stammt aus dem Pfad; der HTTP-API-Aufruf verpackt template und id in JSON.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"JSON-Objekt mit HTML-Zeichenfolge template und Rechnungs-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"template":"
Beispielkurs
","id":"invoice_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"template":{"type":"string"},"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/21","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":258,"reason":"APIPermissions resource mapping to invo.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Reads invoice-linked transaction/payment snapshots and formats an HTML booking/payment fragment without persisting it.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/21","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":734}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/{id}/validation_qr_invoice QR-Rechnungsdaten validieren Prüft die QR-Rechnungsdaten einer Teilnehmerrechnung und liefert qr_exists sowie menschenlesbare violations. Ohne QR-Daten ist qr_exists=false und violations leer. Externe Rechnungen liefern external_invoice_authority. Quellbedingt wird bei PROFILE_XRECHNUNG_3 bereits vorher die XML-Datei ausgegeben; Clients müssen Content-Type prüfen. - Operation-ID: get_v2_invoice_id_validation_qr_invoice - Dokumentation: /docs/api/v2/operations/get-v2-invoice-id-validation-qr-invoice-2a0b185d4e13/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/'"${PATH_ID}"'/validation_qr_invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/{id}/validation_qr_invoice"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/{id}/validation_qr_invoice" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_id_validation_qr_invoice","summary":"QR-Rechnungsdaten validieren","description":"Prüft die QR-Rechnungsdaten einer Teilnehmerrechnung und liefert qr_exists sowie menschenlesbare violations. Ohne QR-Daten ist qr_exists=false und violations leer. Externe Rechnungen liefern external_invoice_authority. Quellbedingt wird bei PROFILE_XRECHNUNG_3 bereits vorher die XML-Datei ausgegeben; Clients müssen Content-Type prüfen.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["invoice_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"JSON mit Rechnungs-ID, QR-Vorhandensein und Prüfmeldungen; beim XRechnung-Profil XML.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"type":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"qr_exists":{"type":"boolean"},"violations":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"additionalProperties":true},"example":{"error":false,"data":{"id":"invoice_012345abcdef_123456","qr_exists":true,"violations":[]}}},"application/xml":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Invoice.php","line":708,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/22","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":258,"reason":"APIPermissions resource mapping to invo.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Validates QR invoice data from the stored invoice snapshot. It does not persist a new QR reference, regenerate an invoice row or collect money.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/22","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":708}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/list Rechnungen des Kontos anzeigen Liest Teilnehmerrechnungen mit Betrag, Zahlungssumme, Fälligkeit, Status und gegebenenfalls externer Buchhaltungszuständigkeit. Unterstützt allgemeine Feldselektion, Sortierung und Pagination sowie Filter nach transaction und offer; offer_start_date_filtered bezieht sich auf den offer-Filter. Auch die ID-Selektion liefert standardmäßig eine ID-indizierte Sammlung. fields=["data"] ergänzt den gespeicherten Rechnungsstand. - Operation-ID: get_v2_invoice_list - Dokumentation: /docs/api/v2/operations/get-v2-invoice-list-a420c5b30369/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_list","summary":"Rechnungen des Kontos anzeigen","description":"Liest Teilnehmerrechnungen mit Betrag, Zahlungssumme, Fälligkeit, Status und gegebenenfalls externer Buchhaltungszuständigkeit. Unterstützt allgemeine Feldselektion, Sortierung und Pagination sowie Filter nach transaction und offer; offer_start_date_filtered bezieht sich auf den offer-Filter. Auch die ID-Selektion liefert standardmäßig eine ID-indizierte Sammlung. fields=[\"data\"] ergänzt den gespeicherten Rechnungsstand.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Rechnungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"invoice_012345abcdef_123456":{"id":"invoice_012345abcdef_123456","amount":120,"amount_pay":40,"currency":"CHF","number":"INV-EXAMPLE","description":"Beispielkurs","user":"user_012345abcdef_123456","paytrans":"paytrans_012345abcdef_123456","date_create":"2026-10-15T10:00:00+02:00","date_payuntil":"2026-11-15T00:00:00+01:00","date_pay":false,"date_cancelled":false,"status":1}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"date_payuntil":{"anyOf":[{"type":"string"},{"const":false}]},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"date_cancelled":{"anyOf":[{"type":"string"},{"const":false}]},"status":{"type":"integer"},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]},"offer_start_date_filtered":{"anyOf":[{"type":"string"},{"const":false}]},"data":{"description":"Optionales gespeichertes Rechnungsdatenobjekt bei fields=[\"data\"]; Inhalt entspricht dem damaligen Rechnungsstand.","type":["object","array","null"]},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"date_payuntil":{"anyOf":[{"type":"string"},{"const":false}]},"date_pay":{"anyOf":[{"type":"string"},{"const":false}]},"date_cancelled":{"anyOf":[{"type":"string"},{"const":false}]},"status":{"type":"integer"},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]},"offer_start_date_filtered":{"anyOf":[{"type":"string"},{"const":false}]},"data":{"description":"Optionales gespeichertes Rechnungsdatenobjekt bei fields=[\"data\"]; Inhalt entspricht dem damaligen Rechnungsstand.","type":["object","array","null"]},"accounting":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/list/invoice_collective_pdf_export Rechnungen als Sammel-PDF exportieren Erzeugt ein gemeinsames PDF aus bis zu 500 Rechnungen und Belegen im Erstellungszeitraum und überspringt extern geführte Rechnungen. storage=download liefert die Datei. securefilemanager erfordert zusätzliche settings-Schreibrechte und antwortet zunächst processing, während der Server die Datei erstellt und dort speichert. Ein noch laufender Export liefert existing_job_still_running. Der GET schreibt den Job-Cache und kann eine Datei im Konto anlegen. - Operation-ID: get_v2_invoice_list_invoice_collective_pdf_export - Dokumentation: /docs/api/v2/operations/get-v2-invoice-list-invoice-collective-pdf-export-dae20655912a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | from | query | ja | Beginn des Erstellungszeitraums, mit strtotime parsebar. | | to | query | ja | Ende des Erstellungszeitraums; muss nach from liegen. | | storage | query | ja | Ziel des Exports. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"from","in":"query","required":true,"description":"Beginn des Erstellungszeitraums, mit strtotime parsebar.","schema":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"example":"2026-10-01T00:00:00+02:00"},{"name":"to","in":"query","required":true,"description":"Ende des Erstellungszeitraums; muss nach from liegen.","schema":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"example":"2026-10-31T23:59:59+01:00"},{"name":"storage","in":"query","required":true,"description":"Ziel des Exports.","schema":{"type":"string","enum":["download","securefilemanager"]},"example":"download"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FROM:?QUERY_FROM setzen}" : "${QUERY_TO:?QUERY_TO setzen}" : "${QUERY_STORAGE:?QUERY_STORAGE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/list/invoice_collective_pdf_export' \ --url-query 'from='"${QUERY_FROM}" \ --url-query 'to='"${QUERY_TO}" \ --url-query 'storage='"${QUERY_STORAGE}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/list/invoice_collective_pdf_export"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("from", required("QUERY_FROM")); url.searchParams.append("to", required("QUERY_TO")); url.searchParams.append("storage", required("QUERY_STORAGE")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/list/invoice_collective_pdf_export" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("from", os.environ["QUERY_FROM"]), ("to", os.environ["QUERY_TO"]), ("storage", os.environ["QUERY_STORAGE"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_list_invoice_collective_pdf_export","summary":"Rechnungen als Sammel-PDF exportieren","description":"Erzeugt ein gemeinsames PDF aus bis zu 500 Rechnungen und Belegen im Erstellungszeitraum und überspringt extern geführte Rechnungen. storage=download liefert die Datei. securefilemanager erfordert zusätzliche settings-Schreibrechte und antwortet zunächst processing, während der Server die Datei erstellt und dort speichert. Ein noch laufender Export liefert existing_job_still_running. Der GET schreibt den Job-Cache und kann eine Datei im Konto anlegen.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"from","in":"query","required":true,"description":"Beginn des Erstellungszeitraums, mit strtotime parsebar.","schema":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"example":"2026-10-01T00:00:00+02:00"},{"name":"to","in":"query","required":true,"description":"Ende des Erstellungszeitraums; muss nach from liegen.","schema":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"example":"2026-10-31T23:59:59+01:00"},{"name":"storage","in":"query","required":true,"description":"Ziel des Exports.","schema":{"type":"string","enum":["download","securefilemanager"]},"example":"download"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"PDF-Download, JSON status=processing bei Dateimanager-Ziel oder existing_job_still_running sowie JSON-Fehler.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"type":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"string","enum":["processing","existing_job_still_running"]}},"additionalProperties":true}},"additionalProperties":true}},"application/pdf":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":258,"reason":"APIPermissions resource mapping to invo.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"storage=download only renders a document; storage=securefilemanager persists a new account file and uploads it to private object storage, including asynchronous work after the response. The native helper now requires current settings PUT capability and a non-readonly context before that persistent branch.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/16","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/16","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-query":{"storage":"download"},"x-edoobox-conditionalAdditionalNativePermission":{"when":{"query":{"storage":{"const":"securefilemanager"}}},"short":"setti","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Invoicefunc.php","method":"_getCollectivePDFExport"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":718}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/list/invoice_collective_xrechnung_export XRechnungen gesammelt exportieren Exportiert bis zu 1000 lokale Rechnungs- und Korrekturdokumente des Erstellungszeitraums als ZIP mit einzelnen XRechnung-XML-Dateien und manifest.json. from/to bestimmen inklusive den Zeitraum; storage muss download sein. Überspringt extern geführte und ungeeignete Dokumente; ohne passende XML-Dokumente no_xrechnung_found. - Operation-ID: get_v2_invoice_list_invoice_collective_xrechnung_export - Dokumentation: /docs/api/v2/operations/get-v2-invoice-list-invoice-collective-xrechnung-export-53f0c539d250/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | storage | query | ja | Ziel des Exports. | | from | query | ja | Beginn des Erstellungszeitraums, mit strtotime parsebar. | | to | query | ja | Ende des Erstellungszeitraums; muss nach from liegen. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"storage","in":"query","required":true,"description":"Ziel des Exports.","schema":{"type":"string","enum":["download"]},"example":"download"},{"name":"from","in":"query","required":true,"description":"Beginn des Erstellungszeitraums, mit strtotime parsebar.","schema":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"example":"2026-10-01T00:00:00+02:00"},{"name":"to","in":"query","required":true,"description":"Ende des Erstellungszeitraums; muss nach from liegen.","schema":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"example":"2026-10-31T23:59:59+01:00"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_STORAGE:?QUERY_STORAGE setzen}" : "${QUERY_FROM:?QUERY_FROM setzen}" : "${QUERY_TO:?QUERY_TO setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/list/invoice_collective_xrechnung_export' \ --url-query 'storage='"${QUERY_STORAGE}" \ --url-query 'from='"${QUERY_FROM}" \ --url-query 'to='"${QUERY_TO}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/list/invoice_collective_xrechnung_export"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("storage", required("QUERY_STORAGE")); url.searchParams.append("from", required("QUERY_FROM")); url.searchParams.append("to", required("QUERY_TO")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/list/invoice_collective_xrechnung_export" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("storage", os.environ["QUERY_STORAGE"]), ("from", os.environ["QUERY_FROM"]), ("to", os.environ["QUERY_TO"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_list_invoice_collective_xrechnung_export","summary":"XRechnungen gesammelt exportieren","description":"Exportiert bis zu 1000 lokale Rechnungs- und Korrekturdokumente des Erstellungszeitraums als ZIP mit einzelnen XRechnung-XML-Dateien und manifest.json. from/to bestimmen inklusive den Zeitraum; storage muss download sein. Überspringt extern geführte und ungeeignete Dokumente; ohne passende XML-Dokumente no_xrechnung_found.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"storage","in":"query","required":true,"description":"Ziel des Exports.","schema":{"type":"string","enum":["download"]},"example":"download"},{"name":"from","in":"query","required":true,"description":"Beginn des Erstellungszeitraums, mit strtotime parsebar.","schema":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"example":"2026-10-01T00:00:00+02:00"},{"name":"to","in":"query","required":true,"description":"Ende des Erstellungszeitraums; muss nach from liegen.","schema":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"example":"2026-10-31T23:59:59+01:00"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ZIP mit XRechnung-Dateien und Manifest; JSON bei Fehlern.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true}},"application/zip":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Invoicefunc.php","line":2638},"reason":"Refresh-suggested compares data only; XRechnung export uses a temporary ZIP deleted after streaming."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":721}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/list/invoice.pdf Rechnungsdokument herunterladen Erzeugt die gewählte Teilnehmerrechnung als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Extern geführte Rechnungen sind gesperrt und liefern external_invoice_authority. - Operation-ID: get_v2_invoice_list_invoice_pdf - Dokumentation: /docs/api/v2/operations/get-v2-invoice-list-invoice-pdf-ffdb117eb9ee/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erster Filter muss die öffentliche invoice-ID in value enthalten. | | pdf | query | nein | Darstellung des erzeugten Dokuments; andere Werte verwenden download. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erster Filter muss die öffentliche invoice-ID in value enthalten.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"invoice_012345abcdef_123456"}]}}},{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/list/invoice.pdf' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/list/invoice.pdf"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/list/invoice.pdf" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_list_invoice_pdf","summary":"Rechnungsdokument herunterladen","description":"Erzeugt die gewählte Teilnehmerrechnung als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Extern geführte Rechnungen sind gesperrt und liefern external_invoice_authority.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erster Filter muss die öffentliche invoice-ID in value enthalten.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"invoice_012345abcdef_123456"}]}}},{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"PDF- oder XRechnung-XML-Dokument; Fehler verwenden die JSON-Fehlerhülle.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}},"application/xml":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":692}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/list/receipt Rechnungsbelege und Stornorechnungen anzeigen Liest Rechnungsbelege aus dem eigenen Konto. id hat den Typ invoicereceipt, invoice verweist auf die Teilnehmerrechnung; type unterscheidet receipt und cancel_receipt. Ein invoice-Filter begrenzt auf eine Rechnung. fields=["data"] ergänzt den gespeicherten Beleginhalt. - Operation-ID: get_v2_invoice_list_receipt - Dokumentation: /docs/api/v2/operations/get-v2-invoice-list-receipt-94fdfc78e4df/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/list/receipt' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/list/receipt"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/list/receipt" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_list_receipt","summary":"Rechnungsbelege und Stornorechnungen anzeigen","description":"Liest Rechnungsbelege aus dem eigenen Konto. id hat den Typ invoicereceipt, invoice verweist auf die Teilnehmerrechnung; type unterscheidet receipt und cancel_receipt. Ein invoice-Filter begrenzt auf eine Rechnung. fields=[\"data\"] ergänzt den gespeicherten Beleginhalt.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"invoice_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von invoicereceipt-Belegen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"invoicereceipt_012345abcdef_123456":{"id":"invoicereceipt_012345abcdef_123456","invoice":"invoice_012345abcdef_123456","amount":120,"currency":"CHF","number":"REC-EXAMPLE","user":"user_012345abcdef_123456","date_create":"2026-10-15T10:00:00+02:00","type":"receipt"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"invoice":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string","enum":["receipt","cancel_receipt"]},"data":{"type":["object","array","null"]},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]},"invoice":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"currency":{"type":"string"},"number":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string","enum":["receipt","cancel_receipt"]},"data":{"type":["object","array","null"]},"offer_start_date":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/17","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":258,"reason":"APIPermissions resource mapping to invo.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Lists existing invoice receipt records and optional stored snapshot/offer date columns without creating any receipt.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/17","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":579}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/list/receipt/invoicereceipt.pdf Rechnungsbeleg herunterladen Erzeugt den gewählten Rechnungsbeleg als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Die ID bezeichnet einen invoicereceipt-Beleg. - Operation-ID: get_v2_invoice_list_receipt_invoicereceipt_pdf - Dokumentation: /docs/api/v2/operations/get-v2-invoice-list-receipt-invoicereceipt-pdf-4f21cc2fc1d3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erster Filter muss die öffentliche invoicereceipt-ID in value enthalten. | | pdf | query | nein | Darstellung des erzeugten Dokuments; andere Werte verwenden download. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erster Filter muss die öffentliche invoicereceipt-ID in value enthalten.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"invoicereceipt_012345abcdef_123456"}]}}},{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/list/receipt/invoicereceipt.pdf' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/list/receipt/invoicereceipt.pdf"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/list/receipt/invoicereceipt.pdf" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_list_receipt_invoicereceipt_pdf","summary":"Rechnungsbeleg herunterladen","description":"Erzeugt den gewählten Rechnungsbeleg als PDF zum Herunterladen oder Inline-Anzeigen. Beim Profil PROFILE_XRECHNUNG_3 wird stattdessen eine XML-Datei ausgegeben. Die ID bezeichnet einen invoicereceipt-Beleg.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erster Filter muss die öffentliche invoicereceipt-ID in value enthalten.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^invoicereceipt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoicereceipt_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"invoicereceipt_012345abcdef_123456"}]}}},{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download"},"description":"Darstellung des erzeugten Dokuments; andere Werte verwenden download.","example":"download"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"PDF- oder XRechnung-XML-Dokument; Fehler verwenden die JSON-Fehlerhülle.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}},"application/xml":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":675}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/list/refund_review Rechnungsdokumente nach Online-Erstattungen prüfen Liefert nicht stornierte Rechnungen mit erfolgreichen Online-Erstattungen und vergleicht aktuellen Zahlungsstand mit dem gespeicherten Dokument. document_differs kennzeichnet eine Abweichung, requires_review negative Zahlungssummen oder manuelle Erstattungseinträge. Ändert weder Rechnung noch Zahlung. limit.start/limit.reply paginieren; reply wird auf 1 bis 100 begrenzt. - Operation-ID: get_v2_invoice_list_refund_review - Dokumentation: /docs/api/v2/operations/get-v2-invoice-list-refund-review-f5fce45baa46/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | limit | query | nein | Pagination für die Prüfung; Standard start=0 und reply=100. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"limit","in":"query","required":false,"description":"Pagination für die Prüfung; Standard start=0 und reply=100.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer"},"reply":{"type":"integer"}},"additionalProperties":true},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/list/refund_review' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/list/refund_review"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/list/refund_review" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_list_refund_review","summary":"Rechnungsdokumente nach Online-Erstattungen prüfen","description":"Liefert nicht stornierte Rechnungen mit erfolgreichen Online-Erstattungen und vergleicht aktuellen Zahlungsstand mit dem gespeicherten Dokument. document_differs kennzeichnet eine Abweichung, requires_review negative Zahlungssummen oder manuelle Erstattungseinträge. Ändert weder Rechnung noch Zahlung. limit.start/limit.reply paginieren; reply wird auf 1 bis 100 begrenzt.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"Pagination für die Prüfung; Standard start=0 und reply=100.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer"},"reply":{"type":"integer"}},"additionalProperties":true},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Prüfliste mit has_more und next_start.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"invoices":[],"has_more":false,"next_start":null}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"number":{"type":"string"},"currency":{"type":"string"},"amount":{"type":"number"},"amount_pay":{"type":"number"},"amount_open":{"type":"number"},"stored_amount_pay":{"type":["number","null"]},"document_differs":{"type":"boolean"},"requires_review":{"type":"boolean"}},"additionalProperties":true}},"has_more":{"type":"boolean"},"next_start":{"type":["integer","null"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":730}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/invoice/list/validation_qr_invoice QR-Rechnungsdaten validieren Prüft die QR-Rechnungsdaten einer Teilnehmerrechnung und liefert qr_exists sowie menschenlesbare violations. Ohne QR-Daten ist qr_exists=false und violations leer. Externe Rechnungen liefern external_invoice_authority. Quellbedingt wird bei PROFILE_XRECHNUNG_3 bereits vorher die XML-Datei ausgegeben; Clients müssen Content-Type prüfen. - Operation-ID: get_v2_invoice_list_validation_qr_invoice - Dokumentation: /docs/api/v2/operations/get-v2-invoice-list-validation-qr-invoice-60c5890f8129/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Erster Filter enthält die Rechnungs-ID in value. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Erster Filter enthält die Rechnungs-ID in value.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"invoice_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/invoice/list/validation_qr_invoice' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/invoice/list/validation_qr_invoice"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/invoice/list/validation_qr_invoice" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_invoice_list_validation_qr_invoice","summary":"QR-Rechnungsdaten validieren","description":"Prüft die QR-Rechnungsdaten einer Teilnehmerrechnung und liefert qr_exists sowie menschenlesbare violations. Ohne QR-Daten ist qr_exists=false und violations leer. Externe Rechnungen liefern external_invoice_authority. Quellbedingt wird bei PROFILE_XRECHNUNG_3 bereits vorher die XML-Datei ausgegeben; Clients müssen Content-Type prüfen.","tags":["invoice"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Erster Filter enthält die Rechnungs-ID in value.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"invoice_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"JSON mit Rechnungs-ID, QR-Vorhandensein und Prüfmeldungen; beim XRechnung-Profil XML.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"type":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},"qr_exists":{"type":"boolean"},"violations":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"additionalProperties":true},"example":{"error":false,"data":{"id":"invoice_012345abcdef_123456","qr_exists":true,"violations":[]}}},"application/xml":{"schema":{"type":"string","format":"binary"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Invoice.php","method":"getInvoice","line":443,"branch":{"file":"application/models/v2/Invoice.php","line":708}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"invoice","short":"invo","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/layout/{id}/pdf PDF-Layout entfernen Markiert das eigene Layout als gelöscht; es erscheint anschließend nicht mehr in aktiven Layoutabfragen. - Operation-ID: delete_v2_layout_id_pdf - Dokumentation: /docs/api/v2/operations/delete-v2-layout-id-pdf-8e9345149af9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_layout_id_pdf","summary":"PDF-Layout entfernen","description":"Markiert das eigene Layout als gelöscht; es erscheint anschließend nicht mehr in aktiven Layoutabfragen.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete beschreiben die entfernte Layout- bzw. Uploadobjekt-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdf_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"PDF-Layout entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"deleteLayout","line":559},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/pdf PDF-Layout lesen Liest gespeicherte PDF-Layouts mit Name, HTML, Dateinamenmuster, Ausrichtung und Seitenrändern. Die Pfad-ID begrenzt die Auswahl. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_layout_id_pdf - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-pdf-bf2ac3c6dfe1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_pdf","summary":"PDF-Layout lesen","description":"Liest gespeicherte PDF-Layouts mit Name, HTML, Dateinamenmuster, Ausrichtung und Seitenrändern. Die Pfad-ID begrenzt die Auswahl. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung von Layout- bzw. Uploadmetadaten in data sowie limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layoutpdf_012345abcdef_123456":{"id":"layoutpdf_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"status":{"type":"boolean"},"name":{"type":"string"},"file_name":{"type":"string"},"orientation":{"type":"string","enum":["portrait","landscape"]},"page_first_margin":{"oneOf":[{"type":"object","properties":{"top":{"type":"integer"},"right":{"type":"integer"},"bottom":{"type":"integer"},"left":{"type":"integer"}},"additionalProperties":true},{"type":"array","maxItems":0}]},"page_next_margin":{"oneOf":[{"type":"object","properties":{"top":{"type":"integer"},"right":{"type":"integer"},"bottom":{"type":"integer"},"left":{"type":"integer"}},"additionalProperties":true},{"type":"array","maxItems":0}]},"html":{"type":"string"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"status":{"type":"boolean"},"name":{"type":"string"},"file_name":{"type":"string"},"orientation":{"type":"string","enum":["portrait","landscape"]},"page_first_margin":{"oneOf":[{"type":"object","properties":{"top":{"type":"integer"},"right":{"type":"integer"},"bottom":{"type":"integer"},"left":{"type":"integer"}},"additionalProperties":true},{"type":"array","maxItems":0}]},"page_next_margin":{"oneOf":[{"type":"object","properties":{"top":{"type":"integer"},"right":{"type":"integer"},"bottom":{"type":"integer"},"left":{"type":"integer"}},"additionalProperties":true},{"type":"array","maxItems":0}]},"html":{"type":"string"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/3","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Layout.php","line":15,"reason":"Constructor selects the exact segment4 Structure and backing table."},"reason":"Read pdf layout metadata from firma_layout_pdf; the exact route has no render/preview action.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/3","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/layout/{id}/pdf PDF-Layout ändern Ändert einzelne Einstellungen des PDF-Layouts, z.B. Name, HTML, Dateinamenmuster, Ausrichtung oder Seitenränder. Nicht übergebene Felder bleiben erhalten. Ein nicht leeres data mit einem beschreibbaren Feld ist erforderlich. - Operation-ID: put_v2_layout_id_pdf - Dokumentation: /docs/api/v2/operations/put-v2-layout-id-pdf-078cec965c49/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Renamed layout" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Renamed layout" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Renamed layout\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_layout_id_pdf","summary":"PDF-Layout ändern","description":"Ändert einzelne Einstellungen des PDF-Layouts, z.B. Name, HTML, Dateinamenmuster, Ausrichtung oder Seitenränder. Nicht übergebene Felder bleiben erhalten. Ein nicht leeres data mit einem beschreibbaren Feld ist erforderlich.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und update melden, ob das ausgewählte Layout geändert wurde.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true},"examples":{"updated":{"value":{"error":false,"data":{"update":true,"id":"layoutpdf_012345abcdef_123456"}}},"reviewed":{"value":{"error":false,"data":{"id":"layoutpdf_012345abcdef_123456","update":true}}}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":64,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":74,"description":"Internal PDF layout name; required on create."},"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":88,"description":"Download filename template; may contain the account's supported PDF placeholders."},"orientation":{"x-edoobox-knownInputs":["portrait","landscape"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["portrait"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":102,"description":"portrait or landscape is canonical; the getter normalizes other values to portrait."},"page_first_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":130},"page_next_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":156},"html":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":60000,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":183,"description":"HTML template rendered with the account's supported PDF placeholders."},"last_change":false},"additionalProperties":true,"description":"PDF Structure fields. Canonical orientation values normalize; stored text limits are not rejecting JSON maxLength assertions."}},"additionalProperties":true},"examples":{"rename":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"name":"Renamed layout"}}}}}}},"x-edoobox-action-contract":{"review":"layout-pdf-put","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Layout.php","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","methods":["__construct","setStructure_PDF","postLayout","updateLayout"]},{"file":"application/models/v2/API_DATA.php","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","methods":["return_insert_data","return_update_data"]}],"preconditions":["Only the selected account owns or changes the layout; placeholder and template expansion use native account context."],"errors":[{"error":"ED80100","meaning":"Required create fields, rejected getter value, or native write validation failed."},{"error":"ED80023","meaning":"Generic batch create exceeds 100 records."}],"unresolved":["Template placeholders, account data, signed ID integrity and rendering output remain native. Scalar getter coercion is documented without inventing rejecting wire limits."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"updateLayout","line":531},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/pdf/placeholders Platzhalter für PDF- und Tabellenlayouts auflisten Liefert die verfügbaren Platzhalter gruppiert für layout, table_head und table_row. Aktive eigene Tabellen werden als LayoutTable[layouttable-ID] angeboten; weitere Platzhalter hängen vom Konto ab. Eine gegebenenfalls vorhandene Pfad-ID wird für diese Auswahl nicht verwendet. - Operation-ID: get_v2_layout_id_pdf_placeholders - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-pdf-placeholders-a4d5f2b2fab0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf/placeholders' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf/placeholders"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf/placeholders" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_pdf_placeholders","summary":"Platzhalter für PDF- und Tabellenlayouts auflisten","description":"Liefert die verfügbaren Platzhalter gruppiert für layout, table_head und table_row. Aktive eigene Tabellen werden als LayoutTable[layouttable-ID] angeboten; weitere Platzhalter hängen vom Konto ab. Eine gegebenenfalls vorhandene Pfad-ID wird für diese Auswahl nicht verwendet.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.layout, table_head und table_row sind jeweils ein Array mit einer Liste von Platzhaltergruppen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layout":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["PresencelistStatusLegend"]}]],"table_head":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["DateStart[d.m.Y]","DateEnd[d.m.Y]"]}]],"table_row":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["UserAttendanceStatus","UserBalance","UserAvatar"]}]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"layout":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}},"table_head":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}},"table_row":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}}},"additionalProperties":true,"required":["layout","table_head","table_row"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/pdf/preview PDF-Layout mit Beispieldaten ansehen Erzeugt eine PDF-Vorschau des gespeicherten Layouts mit synthetischen Angebots-, Termin- und Teilnehmerdaten; ein offer-Selektor wird nicht benötigt. Die Antwort ist binäres application/pdf; pdf=inline zeigt es an, andernfalls wird es heruntergeladen. Die Route speichert keine neue Geschäftsdatei; gelesene Vorlagen können zwischengespeichert werden. - Operation-ID: get_v2_layout_id_pdf_preview - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-pdf-preview-b65913c5f0df/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | pdf | query | nein | Steuert Content-Disposition der PDF-Antwort. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Steuert Content-Disposition der PDF-Antwort.","schema":{"type":"string","enum":["download","inline"],"default":"download","examples":["inline"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf/preview' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf/preview"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf/preview" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_pdf_preview","summary":"PDF-Layout mit Beispieldaten ansehen","description":"Erzeugt eine PDF-Vorschau des gespeicherten Layouts mit synthetischen Angebots-, Termin- und Teilnehmerdaten; ein offer-Selektor wird nicht benötigt. Die Antwort ist binäres application/pdf; pdf=inline zeigt es an, andernfalls wird es heruntergeladen. Die Route speichert keine neue Geschäftsdatei; gelesene Vorlagen können zwischengespeichert werden.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Steuert Content-Disposition der PDF-Antwort.","schema":{"type":"string","enum":["download","inline"],"default":"download","examples":["inline"]}}],"responses":{"200":{"description":"Binäres PDF mit Content-Disposition inline oder attachment; Validierungs- und Berechtigungsfehler bleiben JSON-Antworten.","headers":{"Content-Disposition":{"schema":{"type":"string"},"description":"Dateiname aus dem PDF-Layout, bereinigt und auf 150 Zeichen begrenzt, mit .pdf."}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Layoutfunc.php","line":336,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","method":"_renderLayoutPDF"},"reason":"Queries/templates and PDF inline/download output, without persistent domain changes in this handler."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/pdf/render PDF aus Layout und Angebotsdaten erzeugen Erzeugt ein PDF aus dem gespeicherten Layout, dem ausgewählten Angebot und den zugehörigen Teilnehmer-/Termindaten. data.offer ist erforderlich; data.date_start kann den Startzeitpunkt für Terminspalten bestimmen. Verifizierte Hintergrundvorlagen und Anhänge werden eingebunden. Die Antwort ist binäres application/pdf; pdf=inline zeigt es an, andernfalls wird es heruntergeladen. Die Route speichert keine neue Geschäftsdatei; gelesene Vorlagen können zwischengespeichert werden. - Operation-ID: get_v2_layout_id_pdf_render - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-pdf-render-46791b43674f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | pdf | query | nein | Steuert Content-Disposition der PDF-Antwort. | | data[offer] | query | ja | Öffentliche Angebots-ID als verschachtelter Queryparameter; Beispiel: data[offer]=offer_012345abcdef_123456. | | data[date_start] | query | nein | Optionaler Beginn für Terminspalten; wird mit dP normalisiert. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Steuert Content-Disposition der PDF-Antwort.","schema":{"type":"string","enum":["download","inline"],"default":"download","examples":["inline"]}},{"name":"data[offer]","in":"query","required":true,"schema":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"description":"Öffentliche Angebots-ID als verschachtelter Queryparameter; Beispiel: data[offer]=offer_012345abcdef_123456."},{"name":"data[date_start]","in":"query","required":false,"schema":{"type":"string","examples":["2026-11-01T09:00:00+01:00"]},"description":"Optionaler Beginn für Terminspalten; wird mit dP normalisiert."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${QUERY_DATA_OFFER_:?QUERY_DATA_OFFER_ setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf/render' \ --url-query 'data[offer]='"${QUERY_DATA_OFFER_}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf/render"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("data[offer]", required("QUERY_DATA_OFFER_")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf/render" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("data[offer]", os.environ["QUERY_DATA_OFFER_"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_pdf_render","summary":"PDF aus Layout und Angebotsdaten erzeugen","description":"Erzeugt ein PDF aus dem gespeicherten Layout, dem ausgewählten Angebot und den zugehörigen Teilnehmer-/Termindaten. data.offer ist erforderlich; data.date_start kann den Startzeitpunkt für Terminspalten bestimmen. Verifizierte Hintergrundvorlagen und Anhänge werden eingebunden. Die Antwort ist binäres application/pdf; pdf=inline zeigt es an, andernfalls wird es heruntergeladen. Die Route speichert keine neue Geschäftsdatei; gelesene Vorlagen können zwischengespeichert werden.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Steuert Content-Disposition der PDF-Antwort.","schema":{"type":"string","enum":["download","inline"],"default":"download","examples":["inline"]}},{"name":"data[offer]","in":"query","required":true,"schema":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"description":"Öffentliche Angebots-ID als verschachtelter Queryparameter; Beispiel: data[offer]=offer_012345abcdef_123456."},{"name":"data[date_start]","in":"query","required":false,"schema":{"type":"string","examples":["2026-11-01T09:00:00+01:00"]},"description":"Optionaler Beginn für Terminspalten; wird mit dP normalisiert."}],"responses":{"200":{"description":"Binäres PDF mit Content-Disposition inline oder attachment; Validierungs- und Berechtigungsfehler bleiben JSON-Antworten.","headers":{"Content-Disposition":{"schema":{"type":"string"},"description":"Dateiname aus dem PDF-Layout, bereinigt und auf 150 Zeichen begrenzt, mit .pdf."}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Layoutfunc.php","line":336,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","method":"_renderLayoutPDF"},"reason":"Queries/templates and PDF inline/download output, without persistent domain changes in this handler."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/{id}/pdf/render PDF aus Layout und Angebotsdaten erzeugen Erzeugt ein PDF aus dem gespeicherten Layout, dem ausgewählten Angebot und den zugehörigen Teilnehmer-/Termindaten. data.offer ist erforderlich; data.date_start kann den Startzeitpunkt für Terminspalten bestimmen. Verifizierte Hintergrundvorlagen und Anhänge werden eingebunden. Die Antwort ist binäres application/pdf; pdf=inline zeigt es an, andernfalls wird es heruntergeladen. Die Route speichert keine neue Geschäftsdatei; gelesene Vorlagen können zwischengespeichert werden. - Operation-ID: post_v2_layout_id_pdf_render - Dokumentation: /docs/api/v2/operations/post-v2-layout-id-pdf-render-4616bc2d91c4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | pdf | query | nein | Steuert Content-Disposition der PDF-Antwort. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download","examples":["inline"]},"description":"Steuert Content-Disposition der PDF-Antwort."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "date_start": "2026-11-01T09:00:00+01:00" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf/render' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf/render"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "date_start": "2026-11-01T09:00:00+01:00" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf/render" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"date_start\":\"2026-11-01T09:00:00+01:00\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_id_pdf_render","summary":"PDF aus Layout und Angebotsdaten erzeugen","description":"Erzeugt ein PDF aus dem gespeicherten Layout, dem ausgewählten Angebot und den zugehörigen Teilnehmer-/Termindaten. data.offer ist erforderlich; data.date_start kann den Startzeitpunkt für Terminspalten bestimmen. Verifizierte Hintergrundvorlagen und Anhänge werden eingebunden. Die Antwort ist binäres application/pdf; pdf=inline zeigt es an, andernfalls wird es heruntergeladen. Die Route speichert keine neue Geschäftsdatei; gelesene Vorlagen können zwischengespeichert werden.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"schema":{"type":"string","enum":["download","inline"],"default":"download","examples":["inline"]},"description":"Steuert Content-Disposition der PDF-Antwort."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Binäres PDF mit Content-Disposition inline oder attachment; Validierungs- und Berechtigungsfehler bleiben JSON-Antworten.","headers":{"Content-Disposition":{"schema":{"type":"string"},"description":"Dateiname aus dem PDF-Layout, bereinigt und auf 150 Zeichen begrenzt, mit .pdf."},"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"date_start":{"description":"Optionaler Startzeitpunkt für Terminspalten; dP normalisiert ein Datum bzw. einen Zeitstempel.","examples":["2026-11-01T09:00:00+01:00"]}},"additionalProperties":true,"required":["offer"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"PDF aus Layout und Angebotsdaten erzeugen","value":{"data":{"offer":"offer_012345abcdef_123456","date_start":"2026-11-01T09:00:00+01:00"}}}}}}},"x-edoobox-action-contract":{"review":"layout-pdf-render","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Layout.php","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","methods":["postLayout"]},{"file":"application/models/v2/Layoutfunc.php","sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","methods":["_renderLayoutPDF"]}],"preconditions":["The layout exists in the selected account and is not soft-deleted. Offer/account context and required placeholders must resolve."],"errors":[{"error":"ED80100","meaning":"Missing layout/offer context in native Require_Data."}],"unresolved":["The native guard uses array_search without strict comparison; this contract states supported inputs without claiming every malformed value is rejected there.","Dynamic placeholder inputs, account-dependent PDF composition and binary output remain native."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","line":500,"sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","method":"postLayout"},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/pdf/render/{offer} PDF aus Layout und Angebotsdaten erzeugen Erzeugt das PDF des ausgewählten layoutpdf für das Angebot aus dem zusätzlichen Pfadsegment offer. Verifizierte Anhänge und Hintergrundvorlagen werden eingebunden. Optionales data.date_start steuert den Startzeitpunkt für Terminspalten. Liefert binäres application/pdf als Download oder mit pdf=inline zur Anzeige. - Operation-ID: get_v2_layout_id_pdf_render_offer - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-pdf-render-offer-c1ff588622a9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | pdf | query | nein | Steuert die PDF-Anzeige. | | offer | path | ja | Öffentliche v2-ID des Angebots. | | data[date_start] | query | nein | Optionaler Beginn für Terminspalten; wird mit dP normalisiert. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Steuert die PDF-Anzeige.","schema":{"type":"string","enum":["download","inline"],"default":"download"}},{"name":"offer","in":"path","required":true,"schema":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"description":"Öffentliche v2-ID des Angebots."},{"name":"data[date_start]","in":"query","required":false,"schema":{"type":"string","examples":["2026-11-01T09:00:00+01:00"]},"description":"Optionaler Beginn für Terminspalten; wird mit dP normalisiert."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${PATH_OFFER:?PATH_OFFER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf/render/'"${PATH_OFFER}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf/render/{offer}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); path = path.replaceAll("{offer}", encodeURIComponent(required("PATH_OFFER"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf/render/{offer}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) path = path.replace("{offer}", urllib.parse.quote(os.environ["PATH_OFFER"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_pdf_render_offer","summary":"PDF aus Layout und Angebotsdaten erzeugen","description":"Erzeugt das PDF des ausgewählten layoutpdf für das Angebot aus dem zusätzlichen Pfadsegment offer. Verifizierte Anhänge und Hintergrundvorlagen werden eingebunden. Optionales data.date_start steuert den Startzeitpunkt für Terminspalten. Liefert binäres application/pdf als Download oder mit pdf=inline zur Anzeige.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"pdf","in":"query","required":false,"description":"Steuert die PDF-Anzeige.","schema":{"type":"string","enum":["download","inline"],"default":"download"}},{"name":"offer","in":"path","required":true,"schema":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"description":"Öffentliche v2-ID des Angebots."},{"name":"data[date_start]","in":"query","required":false,"schema":{"type":"string","examples":["2026-11-01T09:00:00+01:00"]},"description":"Optionaler Beginn für Terminspalten; wird mit dP normalisiert."}],"responses":{"200":{"description":"Binäres PDF mit Content-Disposition inline oder attachment; Validierungs- und Berechtigungsfehler bleiben JSON-Antworten.","headers":{"Content-Disposition":{"schema":{"type":"string"},"description":"Dateiname aus dem PDF-Layout, bereinigt und auf 150 Zeichen begrenzt, mit .pdf."}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Layoutfunc.php","line":336,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","method":"_renderLayoutPDF"},"reason":"Queries/templates and PDF inline/download output, without persistent domain changes in this handler."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/layout/{id}/pdf/reset PDF-Layout auf Standardinhalt zurücksetzen Ersetzt die gespeicherten Einstellungen durch die im Handler hinterlegte Standardvorlage: aktive Präsenzliste im Querformat mit Standardname, Dateiname und HTML. Der Request-Body wird ignoriert; die Pfad-ID wählt das zu überschreibende Layout. - Operation-ID: put_v2_layout_id_pdf_reset - Dokumentation: /docs/api/v2/operations/put-v2-layout-id-pdf-reset-f8d7d00fe3f8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf/reset' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf/reset"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf/reset" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_layout_id_pdf_reset","summary":"PDF-Layout auf Standardinhalt zurücksetzen","description":"Ersetzt die gespeicherten Einstellungen durch die im Handler hinterlegte Standardvorlage: aktive Präsenzliste im Querformat mit Standardname, Dateiname und HTML. Der Request-Body wird ignoriert; die Pfad-ID wählt das zu überschreibende Layout.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und update bestätigen die Änderung des ausgewählten Layouts.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdf_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"PDF-Layout auf Standardinhalt zurücksetzen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"updateLayout","line":531},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/layout/{id}/pdf/verify_object Hochgeladenen PDF-Anhang oder Hintergrund prüfen Prüft die zuvor reservierte und hochgeladene PDF-Datei im Speicher. Die ID muss eine layoutpdfattachment- oder layoutpdftemplate-ID sein; der URI-Untertyp wählt den Objekttyp nicht. Der Handler kontrolliert Eigentümer, noch ausstehende Verifikation, MIME-Typ application/pdf, Dateigröße und importierbares PDF-Format. Anhänge dürfen 21 bis 1.500.000 Bytes, Hintergrundvorlagen 21 bis 1.000.000 Bytes haben. Bei Erfolg werden verify und die tatsächliche Größe gespeichert. Ungültige Speicherdateien werden gelöscht; bei PDF-Importfehlern auch der reservierte Metadatensatz. Der Body wird nicht benötigt. - Operation-ID: put_v2_layout_id_pdf_verify_object - Dokumentation: /docs/api/v2/operations/put-v2-layout-id-pdf-verify-object-4357a87026bd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID.","schema":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdf/verify_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdf/verify_object"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "PUT", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdf/verify_object" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_layout_id_pdf_verify_object","summary":"Hochgeladenen PDF-Anhang oder Hintergrund prüfen","description":"Prüft die zuvor reservierte und hochgeladene PDF-Datei im Speicher. Die ID muss eine layoutpdfattachment- oder layoutpdftemplate-ID sein; der URI-Untertyp wählt den Objekttyp nicht. Der Handler kontrolliert Eigentümer, noch ausstehende Verifikation, MIME-Typ application/pdf, Dateigröße und importierbares PDF-Format. Anhänge dürfen 21 bis 1.500.000 Bytes, Hintergrundvorlagen 21 bis 1.000.000 Bytes haben. Bei Erfolg werden verify und die tatsächliche Größe gespeichert. Ungültige Speicherdateien werden gelöscht; bei PDF-Importfehlern auch der reservierte Metadatensatz. Der Body wird nicht benötigt.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID.","schema":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält die öffentliche Uploadobjekt-ID, verify=true und update=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdfattachment_012345abcdef_123456","verify":true,"update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]},"verify":{"const":true},"update":{"const":true}},"additionalProperties":true,"required":["id","verify","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"updateLayout","line":531},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/layout/{id}/pdfattachment PDF-Anhang entfernen Entfernt das hochgeladene Speicherobjekt und dessen Metadatensatz nach Prüfung des zugehörigen eigenen PDF-Layouts. Das funktioniert auch vor verify_object; Speicherfehler verhindern das Entfernen des Metadatenzeigers. - Operation-ID: delete_v2_layout_id_pdfattachment - Dokumentation: /docs/api/v2/operations/delete-v2-layout-id-pdfattachment-dfc30e3779b4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdfattachment' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdfattachment"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdfattachment" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_layout_id_pdfattachment","summary":"PDF-Anhang entfernen","description":"Entfernt das hochgeladene Speicherobjekt und dessen Metadatensatz nach Prüfung des zugehörigen eigenen PDF-Layouts. Das funktioniert auch vor verify_object; Speicherfehler verhindern das Entfernen des Metadatenzeigers.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete beschreiben die entfernte Layout- bzw. Uploadobjekt-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdfattachment_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"PDF-Anhang entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"deleteLayout","line":559},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/pdfattachment PDF-Anhang lesen Liest Metadaten der einem PDF-Layout zugeordneten Uploadobjekte einschließlich layoutpdf, file_name, file_size und verify; Datei-Bytes und Upload-URLs sind nicht enthalten. Die Pfad-ID begrenzt die Auswahl. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_layout_id_pdfattachment - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-pdfattachment-153e7ff45dcb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdfattachment' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdfattachment"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdfattachment" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_pdfattachment","summary":"PDF-Anhang lesen","description":"Liest Metadaten der einem PDF-Layout zugeordneten Uploadobjekte einschließlich layoutpdf, file_name, file_size und verify; Datei-Bytes und Upload-URLs sind nicht enthalten. Die Pfad-ID begrenzt die Auswahl. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Sammlung von Layout- bzw. Uploadmetadaten in data sowie limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layoutpdfattachment_012345abcdef_123456":{"id":"layoutpdfattachment_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdfattachment_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer","description":"Größe der reservierten bzw. verifizierten PDF-Datei in Bytes."},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdfattachment_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer","description":"Größe der reservierten bzw. verifizierten PDF-Datei in Bytes."},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/layout/{id}/pdfattachment/remove_object PDF-Anhang entfernen Entfernt das hochgeladene Speicherobjekt und dessen Metadatensatz nach Prüfung des zugehörigen eigenen PDF-Layouts. Das funktioniert auch vor verify_object; Speicherfehler verhindern das Entfernen des Metadatenzeigers. - Operation-ID: delete_v2_layout_id_pdfattachment_remove_object - Dokumentation: /docs/api/v2/operations/delete-v2-layout-id-pdfattachment-remove-object-8bbcc8b93040/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdfattachment/remove_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdfattachment/remove_object"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdfattachment/remove_object" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_layout_id_pdfattachment_remove_object","summary":"PDF-Anhang entfernen","description":"Entfernt das hochgeladene Speicherobjekt und dessen Metadatensatz nach Prüfung des zugehörigen eigenen PDF-Layouts. Das funktioniert auch vor verify_object; Speicherfehler verhindern das Entfernen des Metadatenzeigers.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete beschreiben die entfernte Layout- bzw. Uploadobjekt-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdfattachment_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"PDF-Anhang entfernen","value":{}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Layout.php","method":"deleteLayout","line":557,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"deleteLayout","line":559},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/layout/{id}/pdfattachment/verify_object Hochgeladenen PDF-Anhang oder Hintergrund prüfen Prüft die zuvor reservierte und hochgeladene PDF-Datei im Speicher. Die ID muss eine layoutpdfattachment- oder layoutpdftemplate-ID sein; der URI-Untertyp wählt den Objekttyp nicht. Der Handler kontrolliert Eigentümer, noch ausstehende Verifikation, MIME-Typ application/pdf, Dateigröße und importierbares PDF-Format. Anhänge dürfen 21 bis 1.500.000 Bytes, Hintergrundvorlagen 21 bis 1.000.000 Bytes haben. Bei Erfolg werden verify und die tatsächliche Größe gespeichert. Ungültige Speicherdateien werden gelöscht; bei PDF-Importfehlern auch der reservierte Metadatensatz. Der Body wird nicht benötigt. - Operation-ID: put_v2_layout_id_pdfattachment_verify_object - Dokumentation: /docs/api/v2/operations/put-v2-layout-id-pdfattachment-verify-object-f24aae8dfa00/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID.","schema":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdfattachment/verify_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdfattachment/verify_object"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "PUT", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdfattachment/verify_object" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_layout_id_pdfattachment_verify_object","summary":"Hochgeladenen PDF-Anhang oder Hintergrund prüfen","description":"Prüft die zuvor reservierte und hochgeladene PDF-Datei im Speicher. Die ID muss eine layoutpdfattachment- oder layoutpdftemplate-ID sein; der URI-Untertyp wählt den Objekttyp nicht. Der Handler kontrolliert Eigentümer, noch ausstehende Verifikation, MIME-Typ application/pdf, Dateigröße und importierbares PDF-Format. Anhänge dürfen 21 bis 1.500.000 Bytes, Hintergrundvorlagen 21 bis 1.000.000 Bytes haben. Bei Erfolg werden verify und die tatsächliche Größe gespeichert. Ungültige Speicherdateien werden gelöscht; bei PDF-Importfehlern auch der reservierte Metadatensatz. Der Body wird nicht benötigt.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID.","schema":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält die öffentliche Uploadobjekt-ID, verify=true und update=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdfattachment_012345abcdef_123456","verify":true,"update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]},"verify":{"const":true},"update":{"const":true}},"additionalProperties":true,"required":["id","verify","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Layoutfunc.php","line":1379,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","method":"_verifyObject"},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"updateLayout","line":531},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/layout/{id}/pdftemplate PDF-Hintergrundvorlage entfernen Entfernt das hochgeladene Speicherobjekt und dessen Metadatensatz nach Prüfung des zugehörigen eigenen PDF-Layouts. Das funktioniert auch vor verify_object; Speicherfehler verhindern das Entfernen des Metadatenzeigers. - Operation-ID: delete_v2_layout_id_pdftemplate - Dokumentation: /docs/api/v2/operations/delete-v2-layout-id-pdftemplate-50183bd4980c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdftemplate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdftemplate"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdftemplate" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_layout_id_pdftemplate","summary":"PDF-Hintergrundvorlage entfernen","description":"Entfernt das hochgeladene Speicherobjekt und dessen Metadatensatz nach Prüfung des zugehörigen eigenen PDF-Layouts. Das funktioniert auch vor verify_object; Speicherfehler verhindern das Entfernen des Metadatenzeigers.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete beschreiben die entfernte Layout- bzw. Uploadobjekt-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdftemplate_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"PDF-Hintergrundvorlage entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"deleteLayout","line":559},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/pdftemplate PDF-Hintergrundvorlage lesen Liest Metadaten der einem PDF-Layout zugeordneten Uploadobjekte einschließlich layoutpdf, file_name, file_size und verify; Datei-Bytes und Upload-URLs sind nicht enthalten. Die Pfad-ID begrenzt die Auswahl. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_layout_id_pdftemplate - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-pdftemplate-ba4788c4fc24/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdftemplate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdftemplate"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdftemplate" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_pdftemplate","summary":"PDF-Hintergrundvorlage lesen","description":"Liest Metadaten der einem PDF-Layout zugeordneten Uploadobjekte einschließlich layoutpdf, file_name, file_size und verify; Datei-Bytes und Upload-URLs sind nicht enthalten. Die Pfad-ID begrenzt die Auswahl. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Sammlung von Layout- bzw. Uploadmetadaten in data sowie limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layoutpdftemplate_012345abcdef_123456":{"id":"layoutpdftemplate_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdftemplate_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer","description":"Größe der reservierten bzw. verifizierten PDF-Datei in Bytes."},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdftemplate_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer","description":"Größe der reservierten bzw. verifizierten PDF-Datei in Bytes."},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/layout/{id}/pdftemplate/remove_object PDF-Hintergrundvorlage entfernen Entfernt das hochgeladene Speicherobjekt und dessen Metadatensatz nach Prüfung des zugehörigen eigenen PDF-Layouts. Das funktioniert auch vor verify_object; Speicherfehler verhindern das Entfernen des Metadatenzeigers. - Operation-ID: delete_v2_layout_id_pdftemplate_remove_object - Dokumentation: /docs/api/v2/operations/delete-v2-layout-id-pdftemplate-remove-object-9a9e0c86aa08/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdftemplate/remove_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdftemplate/remove_object"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdftemplate/remove_object" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_layout_id_pdftemplate_remove_object","summary":"PDF-Hintergrundvorlage entfernen","description":"Entfernt das hochgeladene Speicherobjekt und dessen Metadatensatz nach Prüfung des zugehörigen eigenen PDF-Layouts. Das funktioniert auch vor verify_object; Speicherfehler verhindern das Entfernen des Metadatenzeigers.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete beschreiben die entfernte Layout- bzw. Uploadobjekt-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdftemplate_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"PDF-Hintergrundvorlage entfernen","value":{}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Layout.php","method":"deleteLayout","line":557,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"deleteLayout","line":559},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/layout/{id}/pdftemplate/verify_object Hochgeladenen PDF-Anhang oder Hintergrund prüfen Prüft die zuvor reservierte und hochgeladene PDF-Datei im Speicher. Die ID muss eine layoutpdfattachment- oder layoutpdftemplate-ID sein; der URI-Untertyp wählt den Objekttyp nicht. Der Handler kontrolliert Eigentümer, noch ausstehende Verifikation, MIME-Typ application/pdf, Dateigröße und importierbares PDF-Format. Anhänge dürfen 21 bis 1.500.000 Bytes, Hintergrundvorlagen 21 bis 1.000.000 Bytes haben. Bei Erfolg werden verify und die tatsächliche Größe gespeichert. Ungültige Speicherdateien werden gelöscht; bei PDF-Importfehlern auch der reservierte Metadatensatz. Der Body wird nicht benötigt. - Operation-ID: put_v2_layout_id_pdftemplate_verify_object - Dokumentation: /docs/api/v2/operations/put-v2-layout-id-pdftemplate-verify-object-7406a849ccc4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID.","schema":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/pdftemplate/verify_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/pdftemplate/verify_object"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "PUT", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/pdftemplate/verify_object" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_layout_id_pdftemplate_verify_object","summary":"Hochgeladenen PDF-Anhang oder Hintergrund prüfen","description":"Prüft die zuvor reservierte und hochgeladene PDF-Datei im Speicher. Die ID muss eine layoutpdfattachment- oder layoutpdftemplate-ID sein; der URI-Untertyp wählt den Objekttyp nicht. Der Handler kontrolliert Eigentümer, noch ausstehende Verifikation, MIME-Typ application/pdf, Dateigröße und importierbares PDF-Format. Anhänge dürfen 21 bis 1.500.000 Bytes, Hintergrundvorlagen 21 bis 1.000.000 Bytes haben. Bei Erfolg werden verify und die tatsächliche Größe gespeichert. Ungültige Speicherdateien werden gelöscht; bei PDF-Importfehlern auch der reservierte Metadatensatz. Der Body wird nicht benötigt.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID.","schema":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält die öffentliche Uploadobjekt-ID, verify=true und update=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdftemplate_012345abcdef_123456","verify":true,"update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]},"verify":{"const":true},"update":{"const":true}},"additionalProperties":true,"required":["id","verify","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Layoutfunc.php","line":1379,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","method":"_verifyObject"},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"updateLayout","line":531},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/layout/{id}/table Tabellenlayout entfernen Markiert das eigene Layout als gelöscht; es erscheint anschließend nicht mehr in aktiven Layoutabfragen. - Operation-ID: delete_v2_layout_id_table - Dokumentation: /docs/api/v2/operations/delete-v2-layout-id-table-33e2e69e23bf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/table' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/table"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/table" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_layout_id_table","summary":"Tabellenlayout entfernen","description":"Markiert das eigene Layout als gelöscht; es erscheint anschließend nicht mehr in aktiven Layoutabfragen.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete beschreiben die entfernte Layout- bzw. Uploadobjekt-ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layouttable_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Tabellenlayout entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"deleteLayout","line":559},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/table Tabellenlayout lesen Liest gespeicherte Tabellenlayouts mit Spalten, Platzhaltern, Zeilenmaßen und Leerzeilen. Die Pfad-ID begrenzt die Auswahl. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_layout_id_table - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-table-816e262b2b4b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/table' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/table"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/table" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_table","summary":"Tabellenlayout lesen","description":"Liest gespeicherte Tabellenlayouts mit Spalten, Platzhaltern, Zeilenmaßen und Leerzeilen. Die Pfad-ID begrenzt die Auswahl. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung von Layout- bzw. Uploadmetadaten in data sowie limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layouttable_012345abcdef_123456":{"id":"layouttable_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","examples":["layouttable_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"status":{"type":"boolean"},"name":{"type":"string"},"empty_rows_top":{"type":"integer"},"empty_rows_bottom":{"type":"integer"},"row_minheight_header":{"type":"integer"},"row_minheight_body":{"type":"integer"},"columns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean"},"header_display_css_class":{"type":"string"},"amount_of_entries":{"type":"integer"},"type":{"type":"string","enum":["dates_all","dates_next","participation","custom"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean"},"type_row_css_class":{"type":"string"},"width":{"type":"integer"}},"additionalProperties":true}},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","examples":["layouttable_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"status":{"type":"boolean"},"name":{"type":"string"},"empty_rows_top":{"type":"integer"},"empty_rows_bottom":{"type":"integer"},"row_minheight_header":{"type":"integer"},"row_minheight_body":{"type":"integer"},"columns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean"},"header_display_css_class":{"type":"string"},"amount_of_entries":{"type":"integer"},"type":{"type":"string","enum":["dates_all","dates_next","participation","custom"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean"},"type_row_css_class":{"type":"string"},"width":{"type":"integer"}},"additionalProperties":true}},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/4","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Layout.php","line":15,"reason":"Constructor selects the exact segment4 Structure and backing table."},"reason":"Read table layout metadata from firma_layout_table; the exact route has no render/preview action.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/4","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/layout/{id}/table Tabellenlayout ändern Ändert Name, Leerzeilen, Zeilenmaße oder Spalten des Tabellenlayouts. Bei columns wird die vollständige neue Spaltenliste übergeben und strikt anhand der Spaltenstruktur validiert. Ein nicht leeres data mit einem beschreibbaren Feld ist erforderlich. - Operation-ID: put_v2_layout_id_table - Dokumentation: /docs/api/v2/operations/put-v2-layout-id-table-249b3a596077/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Renamed layout" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/table' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/table"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Renamed layout" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/table" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Renamed layout\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_layout_id_table","summary":"Tabellenlayout ändern","description":"Ändert Name, Leerzeilen, Zeilenmaße oder Spalten des Tabellenlayouts. Bei columns wird die vollständige neue Spaltenliste übergeben und strikt anhand der Spaltenstruktur validiert. Ein nicht leeres data mit einem beschreibbaren Feld ist erforderlich.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und update melden, ob das ausgewählte Layout geändert wurde.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true},"examples":{"updated":{"value":{"error":false,"data":{"update":true,"id":"layouttable_012345abcdef_123456"}}},"reviewed":{"value":{"error":false,"data":{"id":"layouttable_012345abcdef_123456","update":true}}}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":229,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":238},"empty_rows_top":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":251,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"empty_rows_bottom":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":264,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_header":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":277,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_body":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":290,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}}],"description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column.","x-edoobox-hookDefinition":{"class":"Layoutfunc","property":"TableColumns","validator":"validateColumns","description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column."},"x-edoobox-sourceLine":303},"last_change":false},"additionalProperties":true}},"additionalProperties":true},"examples":{"rename":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"name":"Renamed layout"}}}}}}},"x-edoobox-action-contract":{"review":"layout-table-put","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Layout.php","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","methods":["__construct","setStructure_Table","postLayout","updateLayout"]},{"file":"application/models/v2/API_DATA.php","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","methods":["return_insert_data","return_update_data"]},{"file":"application/models/v2/Layoutfunc.php","sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","methods":["_before_addTable","_update_table_columns","validateColumns"]}],"preconditions":["Only the selected account owns or changes the layout; placeholder and template expansion use native account context."],"errors":[{"error":"ED80100","meaning":"Required create fields, rejected getter value, or native write validation failed."},{"error":"ED80023","meaning":"Generic batch create exceeds 100 records."},{"error":"ED80100","error_code":"columns_structure_error"}],"unresolved":["Template placeholders, account data, signed ID integrity and rendering output remain native. Scalar getter coercion is documented without inventing rejecting wire limits."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"updateLayout","line":531},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/{id}/table/placeholders Platzhalter für PDF- und Tabellenlayouts auflisten Liefert die verfügbaren Platzhalter gruppiert für layout, table_head und table_row. Aktive eigene Tabellen werden als LayoutTable[layouttable-ID] angeboten; weitere Platzhalter hängen vom Konto ab. Eine gegebenenfalls vorhandene Pfad-ID wird für diese Auswahl nicht verwendet. - Operation-ID: get_v2_layout_id_table_placeholders - Dokumentation: /docs/api/v2/operations/get-v2-layout-id-table-placeholders-6af8bcb625a0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/table/placeholders' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/table/placeholders"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/table/placeholders" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_id_table_placeholders","summary":"Platzhalter für PDF- und Tabellenlayouts auflisten","description":"Liefert die verfügbaren Platzhalter gruppiert für layout, table_head und table_row. Aktive eigene Tabellen werden als LayoutTable[layouttable-ID] angeboten; weitere Platzhalter hängen vom Konto ab. Eine gegebenenfalls vorhandene Pfad-ID wird für diese Auswahl nicht verwendet.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.layout, table_head und table_row sind jeweils ein Array mit einer Liste von Platzhaltergruppen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layout":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["PresencelistStatusLegend"]}]],"table_head":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["DateStart[d.m.Y]","DateEnd[d.m.Y]"]}]],"table_row":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["UserAttendanceStatus","UserBalance","UserAvatar"]}]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"layout":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}},"table_head":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}},"table_row":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}}},"additionalProperties":true,"required":["layout","table_head","table_row"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/layout/{id}/table/reset Tabellenlayout auf Standardinhalt zurücksetzen Ersetzt die gespeicherten Einstellungen durch die im Handler hinterlegte Standardvorlage: aktive Unterschriftentabelle mit Standardspalten und Zeilenmaßen. Der Request-Body wird ignoriert; die Pfad-ID wählt das zu überschreibende Layout. - Operation-ID: put_v2_layout_id_table_reset - Dokumentation: /docs/api/v2/operations/put-v2-layout-id-table-reset-cee1ab636216/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/table/reset' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/table/reset"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/table/reset" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_layout_id_table_reset","summary":"Tabellenlayout auf Standardinhalt zurücksetzen","description":"Ersetzt die gespeicherten Einstellungen durch die im Handler hinterlegte Standardvorlage: aktive Unterschriftentabelle mit Standardspalten und Zeilenmaßen. Der Request-Body wird ignoriert; die Pfad-ID wählt das zu überschreibende Layout.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und update bestätigen die Änderung des ausgewählten Layouts.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layouttable_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Tabellenlayout auf Standardinhalt zurücksetzen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"updateLayout","line":531},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/layout/{id}/table/verify_object Hochgeladenen PDF-Anhang oder Hintergrund prüfen Prüft die zuvor reservierte und hochgeladene PDF-Datei im Speicher. Die ID muss eine layoutpdfattachment- oder layoutpdftemplate-ID sein; der URI-Untertyp wählt den Objekttyp nicht. Der Handler kontrolliert Eigentümer, noch ausstehende Verifikation, MIME-Typ application/pdf, Dateigröße und importierbares PDF-Format. Anhänge dürfen 21 bis 1.500.000 Bytes, Hintergrundvorlagen 21 bis 1.000.000 Bytes haben. Bei Erfolg werden verify und die tatsächliche Größe gespeichert. Ungültige Speicherdateien werden gelöscht; bei PDF-Importfehlern auch der reservierte Metadatensatz. Der Body wird nicht benötigt. - Operation-ID: put_v2_layout_id_table_verify_object - Dokumentation: /docs/api/v2/operations/put-v2-layout-id-table-verify-object-4bb140e63c4e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID.","schema":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/layout/'"${PATH_ID}"'/table/verify_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/{id}/table/verify_object"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "PUT", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/{id}/table/verify_object" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_layout_id_table_verify_object","summary":"Hochgeladenen PDF-Anhang oder Hintergrund prüfen","description":"Prüft die zuvor reservierte und hochgeladene PDF-Datei im Speicher. Die ID muss eine layoutpdfattachment- oder layoutpdftemplate-ID sein; der URI-Untertyp wählt den Objekttyp nicht. Der Handler kontrolliert Eigentümer, noch ausstehende Verifikation, MIME-Typ application/pdf, Dateigröße und importierbares PDF-Format. Anhänge dürfen 21 bis 1.500.000 Bytes, Hintergrundvorlagen 21 bis 1.000.000 Bytes haben. Bei Erfolg werden verify und die tatsächliche Größe gespeichert. Ungültige Speicherdateien werden gelöscht; bei PDF-Importfehlern auch der reservierte Metadatensatz. Der Body wird nicht benötigt.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"ID des reservierten Uploadobjekts, keine layoutpdf-/layouttable-ID.","schema":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält die öffentliche Uploadobjekt-ID, verify=true und update=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"layoutpdfattachment_012345abcdef_123456","verify":true,"update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]},"verify":{"const":true},"update":{"const":true}},"additionalProperties":true,"required":["id","verify","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"updateLayout","line":531},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/list/pdf PDF-Layout auflisten Liest gespeicherte PDF-Layouts mit Name, HTML, Dateinamenmuster, Ausrichtung und Seitenrändern. filter kann die Auswahl etwa auf layoutpdf begrenzen. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_layout_list_pdf - Dokumentation: /docs/api/v2/operations/get-v2-layout-list-pdf-76bbfdbec515/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/list/pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/pdf"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/pdf" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_list_pdf","summary":"PDF-Layout auflisten","description":"Liest gespeicherte PDF-Layouts mit Name, HTML, Dateinamenmuster, Ausrichtung und Seitenrändern. filter kann die Auswahl etwa auf layoutpdf begrenzen. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung von Layout- bzw. Uploadmetadaten in data sowie limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layoutpdf_012345abcdef_123456":{"id":"layoutpdf_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"status":{"type":"boolean"},"name":{"type":"string"},"file_name":{"type":"string"},"orientation":{"type":"string","enum":["portrait","landscape"]},"page_first_margin":{"oneOf":[{"type":"object","properties":{"top":{"type":"integer"},"right":{"type":"integer"},"bottom":{"type":"integer"},"left":{"type":"integer"}},"additionalProperties":true},{"type":"array","maxItems":0}]},"page_next_margin":{"oneOf":[{"type":"object","properties":{"top":{"type":"integer"},"right":{"type":"integer"},"bottom":{"type":"integer"},"left":{"type":"integer"}},"additionalProperties":true},{"type":"array","maxItems":0}]},"html":{"type":"string"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"status":{"type":"boolean"},"name":{"type":"string"},"file_name":{"type":"string"},"orientation":{"type":"string","enum":["portrait","landscape"]},"page_first_margin":{"oneOf":[{"type":"object","properties":{"top":{"type":"integer"},"right":{"type":"integer"},"bottom":{"type":"integer"},"left":{"type":"integer"}},"additionalProperties":true},{"type":"array","maxItems":0}]},"page_next_margin":{"oneOf":[{"type":"object","properties":{"top":{"type":"integer"},"right":{"type":"integer"},"bottom":{"type":"integer"},"left":{"type":"integer"}},"additionalProperties":true},{"type":"array","maxItems":0}]},"html":{"type":"string"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/23","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":514,"reason":"APIPermissions resource mapping to lyot.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Lists saved PDF layout definitions; this route neither renders a membership/attendance document nor changes layout content.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/23","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/list/pdf PDF-Layout anlegen Erstellt ein wiederverwendbares PDF-Layout. name, file_name, orientation und html sind erforderlich; orientation wird auf portrait oder landscape normalisiert. HTML und Dateiname können die über placeholders angebotenen Platzhalter verwenden. Seitenränder sind optional. data kann ein Objekt oder ein Array mit bis zu 100 Layouts enthalten. - Operation-ID: post_v2_layout_list_pdf - Dokumentation: /docs/api/v2/operations/post-v2-layout-list-pdf-978cfb8b54ef/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example layout", "file_name": "Example_##OfferName##", "orientation": "portrait", "html": "

##OfferName##

" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/list/pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/pdf"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example layout", "file_name": "Example_##OfferName##", "orientation": "portrait", "html": "

##OfferName##

" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/pdf" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example layout\",\"file_name\":\"Example_##OfferName##\",\"orientation\":\"portrait\",\"html\":\"

##OfferName##

\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_list_pdf","summary":"PDF-Layout anlegen","description":"Erstellt ein wiederverwendbares PDF-Layout. name, file_name, orientation und html sind erforderlich; orientation wird auf portrait oder landscape normalisiert. HTML und Dateiname können die über placeholders angebotenen Platzhalter verwenden. Seitenränder sind optional. data kann ein Objekt oder ein Array mit bis zu 100 Layouts enthalten.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert und id; bei einem Datensatz ist id eine öffentliche v2-ID, beim Batch ein Array solcher IDs. Bei fehlgeschlagenem Transaktionsabschluss enthält data insert=false und id=null; error ist ED80100.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"oneOf":[{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"type":"null"}],"description":"Bei erfolgreichem Einzelinsert öffentliche v2-ID, bei erfolgreichem Batch ein Array öffentlicher IDs; bei fehlgeschlagenem Transaktionsabschluss null zusammen mit insert=false und error=ED80100."}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true},"examples":{"created":{"value":{"error":false,"data":{"insert":true,"id":"layoutpdf_012345abcdef_123456"}}},"createdBatch":{"value":{"error":false,"data":{"insert":true,"id":["layoutpdf_012345abcdef_123456","layoutpdf_012345abcdef_123457"]}}},"commitFailure":{"value":{"error":"ED80100","data":{"insert":false,"id":null}}},"reviewed":{"value":{"error":false,"data":{"id":"layoutpdf_012345abcdef_123456","insert":true}}}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":64,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":74,"description":"Internal PDF layout name; required on create."},"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":88,"description":"Download filename template; may contain the account's supported PDF placeholders."},"orientation":{"x-edoobox-knownInputs":["portrait","landscape"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["portrait"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":102,"description":"portrait or landscape is canonical; the getter normalizes other values to portrait."},"page_first_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":130},"page_next_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":156},"html":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":60000,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":183,"description":"HTML template rendered with the account's supported PDF placeholders."},"last_change":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-serverValue":"time()","description":"The getter ignores submitted input and supplies the current server timestamp. No rejecting wire-input type is asserted.","x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":196}},"additionalProperties":true,"description":"PDF Structure fields. Canonical orientation values normalize; stored text limits are not rejecting JSON maxLength assertions.","required":["name","file_name","orientation","html"]},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":64,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":74,"description":"Internal PDF layout name; required on create."},"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":88,"description":"Download filename template; may contain the account's supported PDF placeholders."},"orientation":{"x-edoobox-knownInputs":["portrait","landscape"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["portrait"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":102,"description":"portrait or landscape is canonical; the getter normalizes other values to portrait."},"page_first_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":130},"page_next_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":156},"html":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":60000,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":183,"description":"HTML template rendered with the account's supported PDF placeholders."},"last_change":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-serverValue":"time()","description":"The getter ignores submitted input and supplies the current server timestamp. No rejecting wire-input type is asserted.","x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":196}},"additionalProperties":true,"description":"PDF Structure fields. Canonical orientation values normalize; stored text limits are not rejecting JSON maxLength assertions.","required":["name","file_name","orientation","html"]}}],"description":"Single record or native API_DATA batch (1–100 records), with the same per-record required fields."}},"additionalProperties":true},"examples":{"single":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"name":"Example layout","file_name":"Example_##OfferName##","orientation":"portrait","html":"

##OfferName##

"}}},"batch":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":[{"name":"Example layout","file_name":"Example_##OfferName##","orientation":"portrait","html":"

##OfferName##

"},{"name":"Second example","file_name":"Example_##OfferName##","orientation":"portrait","html":"

##OfferName##

"}]}}}}}},"x-edoobox-action-contract":{"review":"layout-pdf-post","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Layout.php","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","methods":["__construct","setStructure_PDF","postLayout","updateLayout"]},{"file":"application/models/v2/API_DATA.php","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","methods":["return_insert_data","return_update_data"]}],"preconditions":["Only the selected account owns or changes the layout; placeholder and template expansion use native account context."],"errors":[{"error":"ED80100","meaning":"Required create fields, rejected getter value, or native write validation failed."},{"error":"ED80023","meaning":"Generic batch create exceeds 100 records."}],"unresolved":["Template placeholders, account data, signed ID integrity and rendering output remain native. Scalar getter coercion is documented without inventing rejecting wire limits."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"postLayout","line":496},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/list/pdf/placeholders Platzhalter für PDF- und Tabellenlayouts auflisten Liefert die verfügbaren Platzhalter gruppiert für layout, table_head und table_row. Aktive eigene Tabellen werden als LayoutTable[layouttable-ID] angeboten; weitere Platzhalter hängen vom Konto ab. Eine gegebenenfalls vorhandene Pfad-ID wird für diese Auswahl nicht verwendet. - Operation-ID: get_v2_layout_list_pdf_placeholders - Dokumentation: /docs/api/v2/operations/get-v2-layout-list-pdf-placeholders-2661f3ba3812/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter Keine Parameter deklariert. ### Vollständige wirksame Parameterdefinitionen ```json [] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/list/pdf/placeholders' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/pdf/placeholders"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/pdf/placeholders" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_list_pdf_placeholders","summary":"Platzhalter für PDF- und Tabellenlayouts auflisten","description":"Liefert die verfügbaren Platzhalter gruppiert für layout, table_head und table_row. Aktive eigene Tabellen werden als LayoutTable[layouttable-ID] angeboten; weitere Platzhalter hängen vom Konto ab. Eine gegebenenfalls vorhandene Pfad-ID wird für diese Auswahl nicht verwendet.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[],"responses":{"200":{"description":"data.layout, table_head und table_row sind jeweils ein Array mit einer Liste von Platzhaltergruppen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layout":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["PresencelistStatusLegend"]}]],"table_head":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["DateStart[d.m.Y]","DateEnd[d.m.Y]"]}]],"table_row":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["UserAttendanceStatus","UserBalance","UserAvatar"]}]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"layout":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}},"table_head":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}},"table_row":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}}},"additionalProperties":true,"required":["layout","table_head","table_row"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/24","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/config/config.php","line":514,"reason":"APIPermissions resource mapping to lyot.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},"reason":"Returns available layout placeholder keys and existing active table-layout references; it does not render, save or repair a layout.","review":{"file":"docs/openapi/reviews/vue-get-permission-02.json","pointer":"/operations/24","sha256":"25310a80e8f92a0486b6f7538bd2165e0bb66dd59c60a097ee53cf8d950337fc","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/list/pdfattachment PDF-Anhang auflisten Liest Metadaten der einem PDF-Layout zugeordneten Uploadobjekte einschließlich layoutpdf, file_name, file_size und verify; Datei-Bytes und Upload-URLs sind nicht enthalten. filter kann die Auswahl etwa auf layoutpdf begrenzen. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_layout_list_pdfattachment - Dokumentation: /docs/api/v2/operations/get-v2-layout-list-pdfattachment-b064c9c3ddae/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/list/pdfattachment' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/pdfattachment"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/pdfattachment" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_list_pdfattachment","summary":"PDF-Anhang auflisten","description":"Liest Metadaten der einem PDF-Layout zugeordneten Uploadobjekte einschließlich layoutpdf, file_name, file_size und verify; Datei-Bytes und Upload-URLs sind nicht enthalten. filter kann die Auswahl etwa auf layoutpdf begrenzen. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung von Layout- bzw. Uploadmetadaten in data sowie limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layoutpdfattachment_012345abcdef_123456":{"id":"layoutpdfattachment_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdfattachment_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer","description":"Größe der reservierten bzw. verifizierten PDF-Datei in Bytes."},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdfattachment_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer","description":"Größe der reservierten bzw. verifizierten PDF-Datei in Bytes."},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/0","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Layout.php","line":15,"reason":"Constructor selects the exact segment4 Structure and backing table."},"reason":"Read pdfattachment layout metadata from firma_layout_pdf_attachment; the exact route has no render/preview action.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/0","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/list/pdfattachment PDF-Anhang für einen PDF-Upload reservieren Reserviert einen PDF-Anhang oder eine PDF-Hintergrundvorlage für ein eigenes layoutpdf und liefert eine signierte upload_url. Der tatsächliche Objekttyp folgt data.type, unabhängig vom URL-Untertyp. Höchstens zwei Anhänge und eine Hintergrundvorlage pro PDF-Layout sind erlaubt. Dateiname muss mit kleinem .pdf enden und nach SQL-Escaping höchstens 150 Zeichen lang sein; MIME-Typ ist application/pdf. file_size wird zu int normalisiert und muss 21 bis 1.500.000 Bytes für Anhänge bzw. bis 1.000.000 Bytes für Hintergrundvorlagen betragen. Nach separatem Upload muss verify_object aufgerufen werden; URLs sind vertraulich. - Operation-ID: post_v2_layout_list_pdfattachment - Dokumentation: /docs/api/v2/operations/post-v2-layout-list-pdfattachment-b2dc7f6a9df5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "layoutpdf": "layoutpdf_012345abcdef_123456", "type": "pdfattachment", "file_name": "Unterlage.pdf", "file_type": "application/pdf", "file_size": 1024 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/list/pdfattachment' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/pdfattachment"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "layoutpdf": "layoutpdf_012345abcdef_123456", "type": "pdfattachment", "file_name": "Unterlage.pdf", "file_type": "application/pdf", "file_size": 1024 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/pdfattachment" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"layoutpdf\":\"layoutpdf_012345abcdef_123456\",\"type\":\"pdfattachment\",\"file_name\":\"Unterlage.pdf\",\"file_type\":\"application/pdf\",\"file_size\":1024}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_list_pdfattachment","summary":"PDF-Anhang für einen PDF-Upload reservieren","description":"Reserviert einen PDF-Anhang oder eine PDF-Hintergrundvorlage für ein eigenes layoutpdf und liefert eine signierte upload_url. Der tatsächliche Objekttyp folgt data.type, unabhängig vom URL-Untertyp. Höchstens zwei Anhänge und eine Hintergrundvorlage pro PDF-Layout sind erlaubt. Dateiname muss mit kleinem .pdf enden und nach SQL-Escaping höchstens 150 Zeichen lang sein; MIME-Typ ist application/pdf. file_size wird zu int normalisiert und muss 21 bis 1.500.000 Bytes für Anhänge bzw. bis 1.000.000 Bytes für Hintergrundvorlagen betragen. Nach separatem Upload muss verify_object aufgerufen werden; URLs sind vertraulich.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert=true, die öffentliche Uploadobjekt-ID und upload_url.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"layoutpdfattachment_012345abcdef_123456","upload_url":"https://storage.example.invalid/synthetic-layout-upload"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"const":true},"id":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]},"upload_url":{"type":"string","format":"uri","description":"Vertrauliche signierte Upload-URL."}},"additionalProperties":true,"required":["insert","id","upload_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque layoutpdf v2 ID returned by this account. Synthetic example is not a real record.","examples":["layoutpdf_012345abcdef_123456"]},"type":{"type":"string","enum":["pdfattachment","pdftemplate"],"description":"The common _addObject handler selects the stored object type from this payload field; both URL branches delegate to the same handler."},"file_name":{"type":"string","minLength":5,"pattern":"\\.pdf$","examples":["attendance.pdf"],"maxLength":150,"description":"Native PDF filename requires raw UTF-8 length<=150 and SQL-escaped storage length<=150 before presign/reservation. Existing lowercase .pdf and size/type rules remain; quote/backslash-rich names can reach the encoded limit earlier."},"file_type":{"const":"application/pdf"},"file_size":{"minimum":21,"maximum":1500000,"description":"PHP-int-Normalisierung; nach der Normalisierung müssen 21 bis 1.500.000 (Anhang) bzw. 1.000.000 (Hintergrund) Bytes vorliegen. Numerische JSON-Zeichenfolgen werden ebenfalls normalisiert.","x-edoobox-canonicalType":"integer"}},"required":["layoutpdf","type","file_name","file_type","file_size"],"additionalProperties":true,"anyOf":[{"properties":{"type":{"const":"pdfattachment"},"file_size":{"maximum":1500000}},"required":["type","file_size"]},{"properties":{"type":{"const":"pdftemplate"},"file_size":{"maximum":1000000}},"required":["type","file_size"]}]}},"required":["data"],"additionalProperties":true},"examples":{"request":{"summary":"PDF-Anhang für einen PDF-Upload reservieren","value":{"data":{"layoutpdf":"layoutpdf_012345abcdef_123456","type":"pdfattachment","file_name":"Unterlage.pdf","file_type":"application/pdf","file_size":1024}}}}}}},"x-edoobox-upload-ticket":{"provider":"s3","bucketEnv":"AWS_S3_BUCKET","regionEnv":"AWS_S3_REGION","urlPointer":"/data/upload_url","pathPrefixTemplate":"/ED{fid}F_{appVersion}/mail/","method":"PUT","minBytes":21,"mimeType":"application/pdf","expiresInSeconds":1800,"source":{"file":"application/libraries/edoobox/integrations/file/File.php","line":296},"maxBytes":1500000,"workflowSelector":{"requestPointer":"/body/data/type","variants":{"pdfattachment":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdfattachment_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdfattachment","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdfattachment_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]},"pdftemplate":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdftemplate_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdftemplate","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdftemplate_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}}},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdfattachment_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdfattachment","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdfattachment_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-upload-input-review":{"rawFilenameMaxLength":150,"escapedFilenameMaxLength":150,"source":{"file":"application/models/v2/Layoutfunc.php","line":1271,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","lineEnd":1277}},"x-edoobox-upload-kind-review":{"requestPointer":"/body/data/type","nativeEffectiveByteLimits":{"pdfattachment":1500000,"pdftemplate":1000000},"minimumBytes":21,"ticketCapMeaning":"Union cap; the request schema enforces the per-kind maximum before native metadata creation and the staged transfer must exactly equal that validated file_size. Runtime must choose the reviewed workflow variant from the unchanged validated request, with no URL/default fallback.","sources":[{"file":"application/models/v2/Layout.php","line":500,"lineEnd":520,"sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","reason":"Both contextual URL branches call the same _addObject; body.data.type selects the actual stored kind."},{"file":"application/models/v2/Layoutfunc.php","line":13,"lineEnd":17,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Native size predicate requires >20 and includes the per-kind maximum: attachment1500000, template1000000."},{"file":"application/models/v2/Layoutfunc.php","line":1187,"lineEnd":1207,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Actual data.type allowlist and own parent selection before reservations."},{"file":"application/models/v2/Layoutfunc.php","line":1279,"lineEnd":1336,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Native integer-cast size validation and selected per-body-kind insert/public child response; valid cross-type URL payloads remain valid."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"postLayout","line":496},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/list/pdftemplate PDF-Hintergrundvorlage auflisten Liest Metadaten der einem PDF-Layout zugeordneten Uploadobjekte einschließlich layoutpdf, file_name, file_size und verify; Datei-Bytes und Upload-URLs sind nicht enthalten. filter kann die Auswahl etwa auf layoutpdf begrenzen. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_layout_list_pdftemplate - Dokumentation: /docs/api/v2/operations/get-v2-layout-list-pdftemplate-b83eefc5690f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/list/pdftemplate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/pdftemplate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/pdftemplate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_list_pdftemplate","summary":"PDF-Hintergrundvorlage auflisten","description":"Liest Metadaten der einem PDF-Layout zugeordneten Uploadobjekte einschließlich layoutpdf, file_name, file_size und verify; Datei-Bytes und Upload-URLs sind nicht enthalten. filter kann die Auswahl etwa auf layoutpdf begrenzen. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung von Layout- bzw. Uploadmetadaten in data sowie limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layoutpdftemplate_012345abcdef_123456":{"id":"layoutpdftemplate_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdftemplate_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer","description":"Größe der reservierten bzw. verifizierten PDF-Datei in Bytes."},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdftemplate_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","examples":["layoutpdf_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"type":"string"},"file_size":{"type":"integer","description":"Größe der reservierten bzw. verifizierten PDF-Datei in Bytes."},"verify":{"type":"boolean"},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/1","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Layout.php","line":15,"reason":"Constructor selects the exact segment4 Structure and backing table."},"reason":"Read pdftemplate layout metadata from firma_layout_pdf_template; the exact route has no render/preview action.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/1","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/list/pdftemplate PDF-Hintergrundvorlage für einen PDF-Upload reservieren Reserviert einen PDF-Anhang oder eine PDF-Hintergrundvorlage für ein eigenes layoutpdf und liefert eine signierte upload_url. Der tatsächliche Objekttyp folgt data.type, unabhängig vom URL-Untertyp. Höchstens zwei Anhänge und eine Hintergrundvorlage pro PDF-Layout sind erlaubt. Dateiname muss mit kleinem .pdf enden und nach SQL-Escaping höchstens 150 Zeichen lang sein; MIME-Typ ist application/pdf. file_size wird zu int normalisiert und muss 21 bis 1.500.000 Bytes für Anhänge bzw. bis 1.000.000 Bytes für Hintergrundvorlagen betragen. Nach separatem Upload muss verify_object aufgerufen werden; URLs sind vertraulich. - Operation-ID: post_v2_layout_list_pdftemplate - Dokumentation: /docs/api/v2/operations/post-v2-layout-list-pdftemplate-72d55a7b59eb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "layoutpdf": "layoutpdf_012345abcdef_123456", "type": "pdftemplate", "file_name": "Unterlage.pdf", "file_type": "application/pdf", "file_size": 1024 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/list/pdftemplate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/pdftemplate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "layoutpdf": "layoutpdf_012345abcdef_123456", "type": "pdftemplate", "file_name": "Unterlage.pdf", "file_type": "application/pdf", "file_size": 1024 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/pdftemplate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"layoutpdf\":\"layoutpdf_012345abcdef_123456\",\"type\":\"pdftemplate\",\"file_name\":\"Unterlage.pdf\",\"file_type\":\"application/pdf\",\"file_size\":1024}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_list_pdftemplate","summary":"PDF-Hintergrundvorlage für einen PDF-Upload reservieren","description":"Reserviert einen PDF-Anhang oder eine PDF-Hintergrundvorlage für ein eigenes layoutpdf und liefert eine signierte upload_url. Der tatsächliche Objekttyp folgt data.type, unabhängig vom URL-Untertyp. Höchstens zwei Anhänge und eine Hintergrundvorlage pro PDF-Layout sind erlaubt. Dateiname muss mit kleinem .pdf enden und nach SQL-Escaping höchstens 150 Zeichen lang sein; MIME-Typ ist application/pdf. file_size wird zu int normalisiert und muss 21 bis 1.500.000 Bytes für Anhänge bzw. bis 1.000.000 Bytes für Hintergrundvorlagen betragen. Nach separatem Upload muss verify_object aufgerufen werden; URLs sind vertraulich.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert=true, die öffentliche Uploadobjekt-ID und upload_url.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"layoutpdftemplate_012345abcdef_123456","upload_url":"https://storage.example.invalid/synthetic-layout-upload"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"const":true},"id":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]},"upload_url":{"type":"string","format":"uri","description":"Vertrauliche signierte Upload-URL."}},"additionalProperties":true,"required":["insert","id","upload_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque layoutpdf v2 ID returned by this account. Synthetic example is not a real record.","examples":["layoutpdf_012345abcdef_123456"]},"type":{"type":"string","enum":["pdfattachment","pdftemplate"],"description":"The common _addObject handler selects the stored object type from this payload field; both URL branches delegate to the same handler."},"file_name":{"type":"string","minLength":5,"pattern":"\\.pdf$","examples":["attendance.pdf"],"maxLength":150,"description":"Native PDF filename requires raw UTF-8 length<=150 and SQL-escaped storage length<=150 before presign/reservation. Existing lowercase .pdf and size/type rules remain; quote/backslash-rich names can reach the encoded limit earlier."},"file_type":{"const":"application/pdf"},"file_size":{"minimum":21,"maximum":1500000,"description":"PHP-int-Normalisierung; nach der Normalisierung müssen 21 bis 1.500.000 (Anhang) bzw. 1.000.000 (Hintergrund) Bytes vorliegen. Numerische JSON-Zeichenfolgen werden ebenfalls normalisiert.","x-edoobox-canonicalType":"integer"}},"required":["layoutpdf","type","file_name","file_type","file_size"],"additionalProperties":true,"anyOf":[{"properties":{"type":{"const":"pdfattachment"},"file_size":{"maximum":1500000}},"required":["type","file_size"]},{"properties":{"type":{"const":"pdftemplate"},"file_size":{"maximum":1000000}},"required":["type","file_size"]}]}},"required":["data"],"additionalProperties":true},"examples":{"request":{"summary":"PDF-Hintergrundvorlage für einen PDF-Upload reservieren","value":{"data":{"layoutpdf":"layoutpdf_012345abcdef_123456","type":"pdftemplate","file_name":"Unterlage.pdf","file_type":"application/pdf","file_size":1024}}}}}}},"x-edoobox-upload-ticket":{"provider":"s3","bucketEnv":"AWS_S3_BUCKET","regionEnv":"AWS_S3_REGION","urlPointer":"/data/upload_url","pathPrefixTemplate":"/ED{fid}F_{appVersion}/mail/","method":"PUT","minBytes":21,"mimeType":"application/pdf","expiresInSeconds":1800,"source":{"file":"application/libraries/edoobox/integrations/file/File.php","line":296},"maxBytes":1500000,"workflowSelector":{"requestPointer":"/body/data/type","variants":{"pdfattachment":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdfattachment_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdfattachment","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdfattachment_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]},"pdftemplate":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdftemplate_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdftemplate","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdftemplate_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}}},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdftemplate_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdftemplate","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdftemplate_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-upload-input-review":{"rawFilenameMaxLength":150,"escapedFilenameMaxLength":150,"source":{"file":"application/models/v2/Layoutfunc.php","line":1271,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","lineEnd":1277}},"x-edoobox-upload-kind-review":{"requestPointer":"/body/data/type","nativeEffectiveByteLimits":{"pdfattachment":1500000,"pdftemplate":1000000},"minimumBytes":21,"ticketCapMeaning":"Union cap; the request schema enforces the per-kind maximum before native metadata creation and the staged transfer must exactly equal that validated file_size. Runtime must choose the reviewed workflow variant from the unchanged validated request, with no URL/default fallback.","sources":[{"file":"application/models/v2/Layout.php","line":500,"lineEnd":520,"sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","reason":"Both contextual URL branches call the same _addObject; body.data.type selects the actual stored kind."},{"file":"application/models/v2/Layoutfunc.php","line":13,"lineEnd":17,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Native size predicate requires >20 and includes the per-kind maximum: attachment1500000, template1000000."},{"file":"application/models/v2/Layoutfunc.php","line":1187,"lineEnd":1207,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Actual data.type allowlist and own parent selection before reservations."},{"file":"application/models/v2/Layoutfunc.php","line":1279,"lineEnd":1336,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Native integer-cast size validation and selected per-body-kind insert/public child response; valid cross-type URL payloads remain valid."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"postLayout","line":496},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/list/table Tabellenlayout auflisten Liest gespeicherte Tabellenlayouts mit Spalten, Platzhaltern, Zeilenmaßen und Leerzeilen. filter kann die Auswahl etwa auf layoutpdf begrenzen. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_layout_list_table - Dokumentation: /docs/api/v2/operations/get-v2-layout-list-table-e5cc37200307/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/list/table' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/table"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/table" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_list_table","summary":"Tabellenlayout auflisten","description":"Liest gespeicherte Tabellenlayouts mit Spalten, Platzhaltern, Zeilenmaßen und Leerzeilen. filter kann die Auswahl etwa auf layoutpdf begrenzen. data ist nach öffentlicher v2-ID indiziert oder mit response_type=array eine Liste.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"layout_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung von Layout- bzw. Uploadmetadaten in data sowie limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layouttable_012345abcdef_123456":{"id":"layouttable_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","examples":["layouttable_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"status":{"type":"boolean"},"name":{"type":"string"},"empty_rows_top":{"type":"integer"},"empty_rows_bottom":{"type":"integer"},"row_minheight_header":{"type":"integer"},"row_minheight_body":{"type":"integer"},"columns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean"},"header_display_css_class":{"type":"string"},"amount_of_entries":{"type":"integer"},"type":{"type":"string","enum":["dates_all","dates_next","participation","custom"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean"},"type_row_css_class":{"type":"string"},"width":{"type":"integer"}},"additionalProperties":true}},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","examples":["layouttable_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"status":{"type":"boolean"},"name":{"type":"string"},"empty_rows_top":{"type":"integer"},"empty_rows_bottom":{"type":"integer"},"row_minheight_header":{"type":"integer"},"row_minheight_body":{"type":"integer"},"columns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean"},"header_display_css_class":{"type":"string"},"amount_of_entries":{"type":"integer"},"type":{"type":"string","enum":["dates_all","dates_next","participation","custom"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean"},"type_row_css_class":{"type":"string"},"width":{"type":"integer"}},"additionalProperties":true}},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/2","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Layout.php","line":15,"reason":"Constructor selects the exact segment4 Structure and backing table."},"reason":"Read table layout metadata from firma_layout_table; the exact route has no render/preview action.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/2","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/list/table Tabellenlayout anlegen Erstellt ein wiederverwendbares Tabellenlayout für PDF-Listen. name, empty_rows_top, empty_rows_bottom, row_minheight_header, row_minheight_body und columns sind erforderlich. Jede Spalte benötigt name, header_display_names, type, type_row_data und ganzzahliges width. type ist dates_all, dates_next, participation oder custom; optionale Spaltenfelder erhalten Standardwerte. data kann ein Objekt oder ein Array mit bis zu 100 Layouts enthalten. - Operation-ID: post_v2_layout_list_table - Dokumentation: /docs/api/v2/operations/post-v2-layout-list-table-b984f2e14c61/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example table", "empty_rows_top": 0, "empty_rows_bottom": 0, "row_minheight_header": 20, "row_minheight_body": 20, "columns": [ { "name": "Name", "header_display_names": "Name", "type": "participation", "type_row_data": "##UserFirstName##", "width": 25 } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/list/table' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/table"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example table", "empty_rows_top": 0, "empty_rows_bottom": 0, "row_minheight_header": 20, "row_minheight_body": 20, "columns": [ { "name": "Name", "header_display_names": "Name", "type": "participation", "type_row_data": "##UserFirstName##", "width": 25 } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/table" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example table\",\"empty_rows_top\":0,\"empty_rows_bottom\":0,\"row_minheight_header\":20,\"row_minheight_body\":20,\"columns\":[{\"name\":\"Name\",\"header_display_names\":\"Name\",\"type\":\"participation\",\"type_row_data\":\"##UserFirstName##\",\"width\":25}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_list_table","summary":"Tabellenlayout anlegen","description":"Erstellt ein wiederverwendbares Tabellenlayout für PDF-Listen. name, empty_rows_top, empty_rows_bottom, row_minheight_header, row_minheight_body und columns sind erforderlich. Jede Spalte benötigt name, header_display_names, type, type_row_data und ganzzahliges width. type ist dates_all, dates_next, participation oder custom; optionale Spaltenfelder erhalten Standardwerte. data kann ein Objekt oder ein Array mit bis zu 100 Layouts enthalten.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert und id; bei einem Datensatz ist id eine öffentliche v2-ID, beim Batch ein Array solcher IDs. Bei fehlgeschlagenem Transaktionsabschluss enthält data insert=false und id=null; error ist ED80100.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"oneOf":[{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"type":"null"}],"description":"Bei erfolgreichem Einzelinsert öffentliche v2-ID, bei erfolgreichem Batch ein Array öffentlicher IDs; bei fehlgeschlagenem Transaktionsabschluss null zusammen mit insert=false und error=ED80100."}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true},"examples":{"created":{"value":{"error":false,"data":{"insert":true,"id":"layouttable_012345abcdef_123456"}}},"createdBatch":{"value":{"error":false,"data":{"insert":true,"id":["layouttable_012345abcdef_123456","layouttable_012345abcdef_123457"]}}},"commitFailure":{"value":{"error":"ED80100","data":{"insert":false,"id":null}}},"reviewed":{"value":{"error":false,"data":{"id":"layouttable_012345abcdef_123456","insert":true}}}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":229,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":238},"empty_rows_top":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":251,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"empty_rows_bottom":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":264,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_header":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":277,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_body":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":290,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}}],"description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column.","x-edoobox-hookDefinition":{"class":"Layoutfunc","property":"TableColumns","validator":"validateColumns","description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column."},"x-edoobox-sourceLine":303},"last_change":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-serverValue":"time()","description":"The getter ignores submitted input and supplies the current server timestamp. No rejecting wire-input type is asserted.","x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":320}},"additionalProperties":true,"required":["name","empty_rows_top","empty_rows_bottom","row_minheight_header","row_minheight_body","columns"]},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":229,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":238},"empty_rows_top":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":251,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"empty_rows_bottom":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":264,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_header":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":277,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_body":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":290,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}}],"description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column.","x-edoobox-hookDefinition":{"class":"Layoutfunc","property":"TableColumns","validator":"validateColumns","description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column."},"x-edoobox-sourceLine":303},"last_change":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-serverValue":"time()","description":"The getter ignores submitted input and supplies the current server timestamp. No rejecting wire-input type is asserted.","x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":320}},"additionalProperties":true,"required":["name","empty_rows_top","empty_rows_bottom","row_minheight_header","row_minheight_body","columns"]}}],"description":"Single record or native API_DATA batch (1–100 records), with the same per-record required fields."}},"additionalProperties":true},"examples":{"single":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"name":"Example table","empty_rows_top":0,"empty_rows_bottom":0,"row_minheight_header":20,"row_minheight_body":20,"columns":[{"name":"Name","header_display_names":"Name","type":"participation","type_row_data":"##UserFirstName##","width":25}]}}},"batch":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":[{"name":"Example table","empty_rows_top":0,"empty_rows_bottom":0,"row_minheight_header":20,"row_minheight_body":20,"columns":[{"name":"Name","header_display_names":"Name","type":"participation","type_row_data":"##UserFirstName##","width":25}]},{"name":"Second example","empty_rows_top":0,"empty_rows_bottom":0,"row_minheight_header":20,"row_minheight_body":20,"columns":[{"name":"Name","header_display_names":"Name","type":"participation","type_row_data":"##UserFirstName##","width":25}]}]}}}}}},"x-edoobox-action-contract":{"review":"layout-table-post","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Layout.php","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","methods":["__construct","setStructure_Table","postLayout","updateLayout"]},{"file":"application/models/v2/API_DATA.php","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","methods":["return_insert_data","return_update_data"]},{"file":"application/models/v2/Layoutfunc.php","sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","methods":["_before_addTable","_update_table_columns","validateColumns"]}],"preconditions":["Only the selected account owns or changes the layout; placeholder and template expansion use native account context."],"errors":[{"error":"ED80100","meaning":"Required create fields, rejected getter value, or native write validation failed."},{"error":"ED80023","meaning":"Generic batch create exceeds 100 records."},{"error":"ED80100","error_code":"columns_structure_error"}],"unresolved":["Template placeholders, account data, signed ID integrity and rendering output remain native. Scalar getter coercion is documented without inventing rejecting wire limits."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"postLayout","line":496},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/layout/list/table/placeholders Platzhalter für PDF- und Tabellenlayouts auflisten Liefert die verfügbaren Platzhalter gruppiert für layout, table_head und table_row. Aktive eigene Tabellen werden als LayoutTable[layouttable-ID] angeboten; weitere Platzhalter hängen vom Konto ab. Eine gegebenenfalls vorhandene Pfad-ID wird für diese Auswahl nicht verwendet. - Operation-ID: get_v2_layout_list_table_placeholders - Dokumentation: /docs/api/v2/operations/get-v2-layout-list-table-placeholders-1bc1e9f8c047/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/layout/list/table/placeholders' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/list/table/placeholders"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/list/table/placeholders" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_layout_list_table_placeholders","summary":"Platzhalter für PDF- und Tabellenlayouts auflisten","description":"Liefert die verfügbaren Platzhalter gruppiert für layout, table_head und table_row. Aktive eigene Tabellen werden als LayoutTable[layouttable-ID] angeboten; weitere Platzhalter hängen vom Konto ab. Eine gegebenenfalls vorhandene Pfad-ID wird für diese Auswahl nicht verwendet.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.layout, table_head und table_row sind jeweils ein Array mit einer Liste von Platzhaltergruppen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"layout":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["PresencelistStatusLegend"]}]],"table_head":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["DateStart[d.m.Y]","DateEnd[d.m.Y]"]}]],"table_row":[[{"key":"template_specific","name":"Aktuelle Vorlage","placeholders":["UserAttendanceStatus","UserBalance","UserAvatar"]}]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"layout":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}},"table_head":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}},"table_row":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"placeholders":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"required":["key","name","placeholders"]}}}},"additionalProperties":true,"required":["layout","table_head","table_row"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"getLayout","line":575},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/post/pdf PDF-Layout anlegen Erstellt ein wiederverwendbares PDF-Layout. name, file_name, orientation und html sind erforderlich; orientation wird auf portrait oder landscape normalisiert. HTML und Dateiname können die über placeholders angebotenen Platzhalter verwenden. Seitenränder sind optional. data kann ein Objekt oder ein Array mit bis zu 100 Layouts enthalten. - Operation-ID: post_v2_layout_post_pdf - Dokumentation: /docs/api/v2/operations/post-v2-layout-post-pdf-9ecf79a3d66e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example layout", "file_name": "Example_##OfferName##", "orientation": "portrait", "html": "

##OfferName##

" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/post/pdf' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/post/pdf"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example layout", "file_name": "Example_##OfferName##", "orientation": "portrait", "html": "

##OfferName##

" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/post/pdf" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example layout\",\"file_name\":\"Example_##OfferName##\",\"orientation\":\"portrait\",\"html\":\"

##OfferName##

\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_post_pdf","summary":"PDF-Layout anlegen","description":"Erstellt ein wiederverwendbares PDF-Layout. name, file_name, orientation und html sind erforderlich; orientation wird auf portrait oder landscape normalisiert. HTML und Dateiname können die über placeholders angebotenen Platzhalter verwenden. Seitenränder sind optional. data kann ein Objekt oder ein Array mit bis zu 100 Layouts enthalten.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert und id; bei einem Datensatz ist id eine öffentliche v2-ID, beim Batch ein Array solcher IDs. Bei fehlgeschlagenem Transaktionsabschluss enthält data insert=false und id=null; error ist ED80100.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"oneOf":[{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdf_012345abcdef_123456"]}},{"type":"null"}],"description":"Bei erfolgreichem Einzelinsert öffentliche v2-ID, bei erfolgreichem Batch ein Array öffentlicher IDs; bei fehlgeschlagenem Transaktionsabschluss null zusammen mit insert=false und error=ED80100."}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true},"examples":{"created":{"value":{"error":false,"data":{"insert":true,"id":"layoutpdf_012345abcdef_123456"}}},"createdBatch":{"value":{"error":false,"data":{"insert":true,"id":["layoutpdf_012345abcdef_123456","layoutpdf_012345abcdef_123457"]}}},"commitFailure":{"value":{"error":"ED80100","data":{"insert":false,"id":null}}},"reviewed":{"value":{"error":false,"data":{"id":"layoutpdf_012345abcdef_123456","insert":true}}}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":64,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":74,"description":"Internal PDF layout name; required on create."},"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":88,"description":"Download filename template; may contain the account's supported PDF placeholders."},"orientation":{"x-edoobox-knownInputs":["portrait","landscape"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["portrait"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":102,"description":"portrait or landscape is canonical; the getter normalizes other values to portrait."},"page_first_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":130},"page_next_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":156},"html":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":60000,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":183,"description":"HTML template rendered with the account's supported PDF placeholders."},"last_change":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-serverValue":"time()","description":"The getter ignores submitted input and supplies the current server timestamp. No rejecting wire-input type is asserted.","x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":196}},"additionalProperties":true,"description":"PDF Structure fields. Canonical orientation values normalize; stored text limits are not rejecting JSON maxLength assertions.","required":["name","file_name","orientation","html"]},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":64,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":74,"description":"Internal PDF layout name; required on create."},"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":88,"description":"Download filename template; may contain the account's supported PDF placeholders."},"orientation":{"x-edoobox-knownInputs":["portrait","landscape"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["portrait"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":102,"description":"portrait or landscape is canonical; the getter normalizes other values to portrait."},"page_first_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":130},"page_next_margin":{"x-edoobox-schemaStatus":"source-reviewed-normalization","description":"Native getter accepts any JSON input. Non-array input uses top=16, right=15, bottom=16, left=15. Array-like input is merged with those defaults; unknown keys are ignored and supplied values that fail is_numeric/value==intval(value) are removed. Kept values are stored as integers. This is normalization and filtering, not a rejecting input shape or integer-range assertion.","examples":[{"top":16,"right":15,"bottom":16,"left":15}],"x-edoobox-sourceLine":156},"html":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":60000,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":183,"description":"HTML template rendered with the account's supported PDF placeholders."},"last_change":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-serverValue":"time()","description":"The getter ignores submitted input and supplies the current server timestamp. No rejecting wire-input type is asserted.","x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":196}},"additionalProperties":true,"description":"PDF Structure fields. Canonical orientation values normalize; stored text limits are not rejecting JSON maxLength assertions.","required":["name","file_name","orientation","html"]}}],"description":"Single record or native API_DATA batch (1–100 records), with the same per-record required fields."}},"additionalProperties":true},"examples":{"single":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"name":"Example layout","file_name":"Example_##OfferName##","orientation":"portrait","html":"

##OfferName##

"}}},"batch":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":[{"name":"Example layout","file_name":"Example_##OfferName##","orientation":"portrait","html":"

##OfferName##

"},{"name":"Second example","file_name":"Example_##OfferName##","orientation":"portrait","html":"

##OfferName##

"}]}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Layout.php","line":500,"reason":"postLayout chooses segment4 Structure and ignores segment3; Vue uses literal post.","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","method":"postLayout"},"x-edoobox-action-contract":{"review":"layout-pdf-post","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Layout.php","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","methods":["__construct","setStructure_PDF","postLayout","updateLayout"]},{"file":"application/models/v2/API_DATA.php","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","methods":["return_insert_data","return_update_data"]}],"preconditions":["Only the selected account owns or changes the layout; placeholder and template expansion use native account context."],"errors":[{"error":"ED80100","meaning":"Required create fields, rejected getter value, or native write validation failed."},{"error":"ED80023","meaning":"Generic batch create exceeds 100 records."}],"unresolved":["Template placeholders, account data, signed ID integrity and rendering output remain native. Scalar getter coercion is documented without inventing rejecting wire limits."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"postLayout","line":496},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/post/pdfattachment/add_object PDF-Anhang für einen PDF-Upload reservieren Reserviert einen PDF-Anhang oder eine PDF-Hintergrundvorlage für ein eigenes layoutpdf und liefert eine signierte upload_url. Der tatsächliche Objekttyp folgt data.type, unabhängig vom URL-Untertyp. Höchstens zwei Anhänge und eine Hintergrundvorlage pro PDF-Layout sind erlaubt. Dateiname muss mit kleinem .pdf enden und nach SQL-Escaping höchstens 150 Zeichen lang sein; MIME-Typ ist application/pdf. file_size wird zu int normalisiert und muss 21 bis 1.500.000 Bytes für Anhänge bzw. bis 1.000.000 Bytes für Hintergrundvorlagen betragen. Nach separatem Upload muss verify_object aufgerufen werden; URLs sind vertraulich. - Operation-ID: post_v2_layout_post_pdfattachment_add_object - Dokumentation: /docs/api/v2/operations/post-v2-layout-post-pdfattachment-add-object-906ebca74e4e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "layoutpdf": "layoutpdf_012345abcdef_123456", "type": "pdfattachment", "file_name": "Unterlage.pdf", "file_type": "application/pdf", "file_size": 1024 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/post/pdfattachment/add_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/post/pdfattachment/add_object"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "layoutpdf": "layoutpdf_012345abcdef_123456", "type": "pdfattachment", "file_name": "Unterlage.pdf", "file_type": "application/pdf", "file_size": 1024 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/post/pdfattachment/add_object" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"layoutpdf\":\"layoutpdf_012345abcdef_123456\",\"type\":\"pdfattachment\",\"file_name\":\"Unterlage.pdf\",\"file_type\":\"application/pdf\",\"file_size\":1024}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_post_pdfattachment_add_object","summary":"PDF-Anhang für einen PDF-Upload reservieren","description":"Reserviert einen PDF-Anhang oder eine PDF-Hintergrundvorlage für ein eigenes layoutpdf und liefert eine signierte upload_url. Der tatsächliche Objekttyp folgt data.type, unabhängig vom URL-Untertyp. Höchstens zwei Anhänge und eine Hintergrundvorlage pro PDF-Layout sind erlaubt. Dateiname muss mit kleinem .pdf enden und nach SQL-Escaping höchstens 150 Zeichen lang sein; MIME-Typ ist application/pdf. file_size wird zu int normalisiert und muss 21 bis 1.500.000 Bytes für Anhänge bzw. bis 1.000.000 Bytes für Hintergrundvorlagen betragen. Nach separatem Upload muss verify_object aufgerufen werden; URLs sind vertraulich.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert=true, die öffentliche Uploadobjekt-ID und upload_url.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"layoutpdfattachment_012345abcdef_123456","upload_url":"https://storage.example.invalid/synthetic-layout-upload"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"const":true},"id":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]},"upload_url":{"type":"string","format":"uri","description":"Vertrauliche signierte Upload-URL."}},"additionalProperties":true,"required":["insert","id","upload_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque layoutpdf v2 ID returned by this account. Synthetic example is not a real record.","examples":["layoutpdf_012345abcdef_123456"]},"type":{"type":"string","enum":["pdfattachment","pdftemplate"],"description":"The common _addObject handler selects the stored object type from this payload field; both URL branches delegate to the same handler."},"file_name":{"type":"string","minLength":5,"pattern":"\\.pdf$","examples":["attendance.pdf"],"maxLength":150,"description":"Native PDF filename requires raw UTF-8 length<=150 and SQL-escaped storage length<=150 before presign/reservation. Existing lowercase .pdf and size/type rules remain; quote/backslash-rich names can reach the encoded limit earlier."},"file_type":{"const":"application/pdf"},"file_size":{"minimum":21,"maximum":1500000,"description":"PHP-int-Normalisierung; nach der Normalisierung müssen 21 bis 1.500.000 (Anhang) bzw. 1.000.000 (Hintergrund) Bytes vorliegen. Numerische JSON-Zeichenfolgen werden ebenfalls normalisiert.","x-edoobox-canonicalType":"integer"}},"required":["layoutpdf","type","file_name","file_type","file_size"],"additionalProperties":true,"anyOf":[{"properties":{"type":{"const":"pdfattachment"},"file_size":{"maximum":1500000}},"required":["type","file_size"]},{"properties":{"type":{"const":"pdftemplate"},"file_size":{"maximum":1000000}},"required":["type","file_size"]}]}},"required":["data"],"additionalProperties":true},"examples":{"request":{"summary":"PDF-Anhang für einen PDF-Upload reservieren","value":{"data":{"layoutpdf":"layoutpdf_012345abcdef_123456","type":"pdfattachment","file_name":"Unterlage.pdf","file_type":"application/pdf","file_size":1024}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Layout.php","line":514,"reason":"postLayout selects the literal segment4 pdfattachment branch and delegates to Layoutfunc::_addObject; this source-backed Vue URL supplies post/add_object as contextual segments.","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","method":"postLayout"},"x-edoobox-upload-ticket":{"provider":"s3","bucketEnv":"AWS_S3_BUCKET","regionEnv":"AWS_S3_REGION","urlPointer":"/data/upload_url","pathPrefixTemplate":"/ED{fid}F_{appVersion}/mail/","method":"PUT","minBytes":21,"mimeType":"application/pdf","expiresInSeconds":1800,"source":{"file":"application/libraries/edoobox/integrations/file/File.php","line":296},"maxBytes":1500000,"workflowSelector":{"requestPointer":"/body/data/type","variants":{"pdfattachment":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdfattachment_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdfattachment","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdfattachment_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]},"pdftemplate":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdftemplate_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdftemplate","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdftemplate_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}}},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdfattachment_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdfattachment","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdfattachment_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-upload-input-review":{"rawFilenameMaxLength":150,"escapedFilenameMaxLength":150,"source":{"file":"application/models/v2/Layoutfunc.php","line":1271,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","lineEnd":1277}},"x-edoobox-upload-kind-review":{"requestPointer":"/body/data/type","nativeEffectiveByteLimits":{"pdfattachment":1500000,"pdftemplate":1000000},"minimumBytes":21,"ticketCapMeaning":"Union cap; the request schema enforces the per-kind maximum before native metadata creation and the staged transfer must exactly equal that validated file_size. Runtime must choose the reviewed workflow variant from the unchanged validated request, with no URL/default fallback.","sources":[{"file":"application/models/v2/Layout.php","line":500,"lineEnd":520,"sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","reason":"Both contextual URL branches call the same _addObject; body.data.type selects the actual stored kind."},{"file":"application/models/v2/Layoutfunc.php","line":13,"lineEnd":17,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Native size predicate requires >20 and includes the per-kind maximum: attachment1500000, template1000000."},{"file":"application/models/v2/Layoutfunc.php","line":1187,"lineEnd":1207,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Actual data.type allowlist and own parent selection before reservations."},{"file":"application/models/v2/Layoutfunc.php","line":1279,"lineEnd":1336,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Native integer-cast size validation and selected per-body-kind insert/public child response; valid cross-type URL payloads remain valid."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"postLayout","line":496},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/post/pdftemplate/add_object PDF-Hintergrundvorlage für einen PDF-Upload reservieren Reserviert einen PDF-Anhang oder eine PDF-Hintergrundvorlage für ein eigenes layoutpdf und liefert eine signierte upload_url. Der tatsächliche Objekttyp folgt data.type, unabhängig vom URL-Untertyp. Höchstens zwei Anhänge und eine Hintergrundvorlage pro PDF-Layout sind erlaubt. Dateiname muss mit kleinem .pdf enden und nach SQL-Escaping höchstens 150 Zeichen lang sein; MIME-Typ ist application/pdf. file_size wird zu int normalisiert und muss 21 bis 1.500.000 Bytes für Anhänge bzw. bis 1.000.000 Bytes für Hintergrundvorlagen betragen. Nach separatem Upload muss verify_object aufgerufen werden; URLs sind vertraulich. - Operation-ID: post_v2_layout_post_pdftemplate_add_object - Dokumentation: /docs/api/v2/operations/post-v2-layout-post-pdftemplate-add-object-99afbd84f107/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "layoutpdf": "layoutpdf_012345abcdef_123456", "type": "pdftemplate", "file_name": "Unterlage.pdf", "file_type": "application/pdf", "file_size": 1024 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/post/pdftemplate/add_object' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/post/pdftemplate/add_object"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "layoutpdf": "layoutpdf_012345abcdef_123456", "type": "pdftemplate", "file_name": "Unterlage.pdf", "file_type": "application/pdf", "file_size": 1024 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/post/pdftemplate/add_object" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"layoutpdf\":\"layoutpdf_012345abcdef_123456\",\"type\":\"pdftemplate\",\"file_name\":\"Unterlage.pdf\",\"file_type\":\"application/pdf\",\"file_size\":1024}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_post_pdftemplate_add_object","summary":"PDF-Hintergrundvorlage für einen PDF-Upload reservieren","description":"Reserviert einen PDF-Anhang oder eine PDF-Hintergrundvorlage für ein eigenes layoutpdf und liefert eine signierte upload_url. Der tatsächliche Objekttyp folgt data.type, unabhängig vom URL-Untertyp. Höchstens zwei Anhänge und eine Hintergrundvorlage pro PDF-Layout sind erlaubt. Dateiname muss mit kleinem .pdf enden und nach SQL-Escaping höchstens 150 Zeichen lang sein; MIME-Typ ist application/pdf. file_size wird zu int normalisiert und muss 21 bis 1.500.000 Bytes für Anhänge bzw. bis 1.000.000 Bytes für Hintergrundvorlagen betragen. Nach separatem Upload muss verify_object aufgerufen werden; URLs sind vertraulich.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert=true, die öffentliche Uploadobjekt-ID und upload_url.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"layoutpdftemplate_012345abcdef_123456","upload_url":"https://storage.example.invalid/synthetic-layout-upload"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"const":true},"id":{"oneOf":[{"type":"string","pattern":"^layoutpdfattachment_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdfattachment_012345abcdef_123456"]},{"type":"string","pattern":"^layoutpdftemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layoutpdftemplate_012345abcdef_123456"]}]},"upload_url":{"type":"string","format":"uri","description":"Vertrauliche signierte Upload-URL."}},"additionalProperties":true,"required":["insert","id","upload_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"layoutpdf":{"type":"string","pattern":"^layoutpdf_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque layoutpdf v2 ID returned by this account. Synthetic example is not a real record.","examples":["layoutpdf_012345abcdef_123456"]},"type":{"type":"string","enum":["pdfattachment","pdftemplate"],"description":"The common _addObject handler selects the stored object type from this payload field; both URL branches delegate to the same handler."},"file_name":{"type":"string","minLength":5,"pattern":"\\.pdf$","examples":["attendance.pdf"],"maxLength":150,"description":"Native PDF filename requires raw UTF-8 length<=150 and SQL-escaped storage length<=150 before presign/reservation. Existing lowercase .pdf and size/type rules remain; quote/backslash-rich names can reach the encoded limit earlier."},"file_type":{"const":"application/pdf"},"file_size":{"minimum":21,"maximum":1500000,"description":"PHP-int-Normalisierung; nach der Normalisierung müssen 21 bis 1.500.000 (Anhang) bzw. 1.000.000 (Hintergrund) Bytes vorliegen. Numerische JSON-Zeichenfolgen werden ebenfalls normalisiert.","x-edoobox-canonicalType":"integer"}},"required":["layoutpdf","type","file_name","file_type","file_size"],"additionalProperties":true,"anyOf":[{"properties":{"type":{"const":"pdfattachment"},"file_size":{"maximum":1500000}},"required":["type","file_size"]},{"properties":{"type":{"const":"pdftemplate"},"file_size":{"maximum":1000000}},"required":["type","file_size"]}]}},"required":["data"],"additionalProperties":true},"examples":{"request":{"summary":"PDF-Hintergrundvorlage für einen PDF-Upload reservieren","value":{"data":{"layoutpdf":"layoutpdf_012345abcdef_123456","type":"pdftemplate","file_name":"Unterlage.pdf","file_type":"application/pdf","file_size":1024}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Layout.php","line":517,"reason":"postLayout selects the literal segment4 pdftemplate branch and delegates to Layoutfunc::_addObject; this source-backed Vue URL supplies post/add_object as contextual segments.","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","method":"postLayout"},"x-edoobox-upload-ticket":{"provider":"s3","bucketEnv":"AWS_S3_BUCKET","regionEnv":"AWS_S3_REGION","urlPointer":"/data/upload_url","pathPrefixTemplate":"/ED{fid}F_{appVersion}/mail/","method":"PUT","minBytes":21,"mimeType":"application/pdf","expiresInSeconds":1800,"source":{"file":"application/libraries/edoobox/integrations/file/File.php","line":296},"maxBytes":1500000,"workflowSelector":{"requestPointer":"/body/data/type","variants":{"pdfattachment":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdfattachment_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdfattachment","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdfattachment_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]},"pdftemplate":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdftemplate_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdftemplate","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdftemplate_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}}},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"object","finalization":{"operationId":"put_v2_layout_id_pdftemplate_verify_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"scope":"object","after":"successful-storage-transfer"},"readback":{"operationId":"get_v2_layout_id_pdftemplate","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"native-verification-record"},"abort":{"operationId":"delete_v2_layout_id_pdftemplate_remove_object","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"successCriteria":{"responsePointers":{"/error":false,"/data/verify":true},"readbackRecord":{"verify":true}},"notes":["Verify, readback and removal use the newly returned layout child ID, never the parent layoutpdf ID.","Native verification checks storage bytes and MIME before marking the child verified. PDF import validation is not a formal PDF/A certification.","Native removal attempts storage deletion for pending and verified uploads before deleting metadata. Storage cleanup is not guaranteed after a reported failure.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-upload-input-review":{"rawFilenameMaxLength":150,"escapedFilenameMaxLength":150,"source":{"file":"application/models/v2/Layoutfunc.php","line":1271,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","lineEnd":1277}},"x-edoobox-upload-kind-review":{"requestPointer":"/body/data/type","nativeEffectiveByteLimits":{"pdfattachment":1500000,"pdftemplate":1000000},"minimumBytes":21,"ticketCapMeaning":"Union cap; the request schema enforces the per-kind maximum before native metadata creation and the staged transfer must exactly equal that validated file_size. Runtime must choose the reviewed workflow variant from the unchanged validated request, with no URL/default fallback.","sources":[{"file":"application/models/v2/Layout.php","line":500,"lineEnd":520,"sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","reason":"Both contextual URL branches call the same _addObject; body.data.type selects the actual stored kind."},{"file":"application/models/v2/Layoutfunc.php","line":13,"lineEnd":17,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Native size predicate requires >20 and includes the per-kind maximum: attachment1500000, template1000000."},{"file":"application/models/v2/Layoutfunc.php","line":1187,"lineEnd":1207,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Actual data.type allowlist and own parent selection before reservations."},{"file":"application/models/v2/Layoutfunc.php","line":1279,"lineEnd":1336,"sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","reason":"Native integer-cast size validation and selected per-body-kind insert/public child response; valid cross-type URL payloads remain valid."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"postLayout","line":496},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/layout/post/table Tabellenlayout anlegen Erstellt ein wiederverwendbares Tabellenlayout für PDF-Listen. name, empty_rows_top, empty_rows_bottom, row_minheight_header, row_minheight_body und columns sind erforderlich. Jede Spalte benötigt name, header_display_names, type, type_row_data und ganzzahliges width. type ist dates_all, dates_next, participation oder custom; optionale Spaltenfelder erhalten Standardwerte. data kann ein Objekt oder ein Array mit bis zu 100 Layouts enthalten. - Operation-ID: post_v2_layout_post_table - Dokumentation: /docs/api/v2/operations/post-v2-layout-post-table-e51196913cf0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example table", "empty_rows_top": 0, "empty_rows_bottom": 0, "row_minheight_header": 20, "row_minheight_body": 20, "columns": [ { "name": "Name", "header_display_names": "Name", "type": "participation", "type_row_data": "##UserFirstName##", "width": 25 } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/layout/post/table' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/layout/post/table"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example table", "empty_rows_top": 0, "empty_rows_bottom": 0, "row_minheight_header": 20, "row_minheight_body": 20, "columns": [ { "name": "Name", "header_display_names": "Name", "type": "participation", "type_row_data": "##UserFirstName##", "width": 25 } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/layout/post/table" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example table\",\"empty_rows_top\":0,\"empty_rows_bottom\":0,\"row_minheight_header\":20,\"row_minheight_body\":20,\"columns\":[{\"name\":\"Name\",\"header_display_names\":\"Name\",\"type\":\"participation\",\"type_row_data\":\"##UserFirstName##\",\"width\":25}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_layout_post_table","summary":"Tabellenlayout anlegen","description":"Erstellt ein wiederverwendbares Tabellenlayout für PDF-Listen. name, empty_rows_top, empty_rows_bottom, row_minheight_header, row_minheight_body und columns sind erforderlich. Jede Spalte benötigt name, header_display_names, type, type_row_data und ganzzahliges width. type ist dates_all, dates_next, participation oder custom; optionale Spaltenfelder erhalten Standardwerte. data kann ein Objekt oder ein Array mit bis zu 100 Layouts enthalten.","tags":["layout"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert und id; bei einem Datensatz ist id eine öffentliche v2-ID, beim Batch ein Array solcher IDs. Bei fehlgeschlagenem Transaktionsabschluss enthält data insert=false und id=null; error ist ED80100.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"oneOf":[{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^layouttable_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["layouttable_012345abcdef_123456"]}},{"type":"null"}],"description":"Bei erfolgreichem Einzelinsert öffentliche v2-ID, bei erfolgreichem Batch ein Array öffentlicher IDs; bei fehlgeschlagenem Transaktionsabschluss null zusammen mit insert=false und error=ED80100."}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true},"examples":{"created":{"value":{"error":false,"data":{"insert":true,"id":"layouttable_012345abcdef_123456"}}},"createdBatch":{"value":{"error":false,"data":{"insert":true,"id":["layouttable_012345abcdef_123456","layouttable_012345abcdef_123457"]}}},"commitFailure":{"value":{"error":"ED80100","data":{"insert":false,"id":null}}},"reviewed":{"value":{"error":false,"data":{"id":"layouttable_012345abcdef_123456","insert":true}}}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":229,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":238},"empty_rows_top":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":251,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"empty_rows_bottom":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":264,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_header":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":277,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_body":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":290,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}}],"description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column.","x-edoobox-hookDefinition":{"class":"Layoutfunc","property":"TableColumns","validator":"validateColumns","description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column."},"x-edoobox-sourceLine":303},"last_change":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-serverValue":"time()","description":"The getter ignores submitted input and supplies the current server timestamp. No rejecting wire-input type is asserted.","x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":320}},"additionalProperties":true,"required":["name","empty_rows_top","empty_rows_bottom","row_minheight_header","row_minheight_body","columns"]},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":229,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":238},"empty_rows_top":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":251,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"empty_rows_bottom":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":264,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_header":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":277,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_minheight_body":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":290,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"columns":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","default":""},"name":{"type":"string"},"header_display_names":{"type":"string"},"header_text_vertical":{"type":"boolean","default":false},"header_display_css_class":{"type":"string","default":""},"amount_of_entries":{"type":"integer","default":1},"type":{"x-edoobox-knownInputs":["dates_all","dates_next","participation","custom"],"x-edoobox-normalization":"Reviewed hook uses PHP loose in_array; these are canonical values, not an exhaustive JSON input enum.","examples":["dates_all"]},"type_row_data":{"type":"string"},"row_text_vertical":{"type":"boolean","default":false},"type_row_css_class":{"type":"string","default":""},"width":{"type":"integer"}},"required":["name","header_display_names","type","type_row_data","width"],"additionalProperties":true}}],"description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column.","x-edoobox-hookDefinition":{"class":"Layoutfunc","property":"TableColumns","validator":"validateColumns","description":"Column array validated by native _before_addTable and _update_table_columns. Per-column required fields and scalar rejection types are derived from TableColumns. Unknown column keys are dropped by the native validator. Both lists and keyed objects are iterable native inputs; each value is validated as one column."},"x-edoobox-sourceLine":303},"last_change":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-serverValue":"time()","description":"The getter ignores submitted input and supplies the current server timestamp. No rejecting wire-input type is asserted.","x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":320}},"additionalProperties":true,"required":["name","empty_rows_top","empty_rows_bottom","row_minheight_header","row_minheight_body","columns"]}}],"description":"Single record or native API_DATA batch (1–100 records), with the same per-record required fields."}},"additionalProperties":true},"examples":{"single":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":{"name":"Example table","empty_rows_top":0,"empty_rows_bottom":0,"row_minheight_header":20,"row_minheight_body":20,"columns":[{"name":"Name","header_display_names":"Name","type":"participation","type_row_data":"##UserFirstName##","width":25}]}}},"batch":{"summary":"Synthetic supported request; replace all IDs with values read from this account.","value":{"data":[{"name":"Example table","empty_rows_top":0,"empty_rows_bottom":0,"row_minheight_header":20,"row_minheight_body":20,"columns":[{"name":"Name","header_display_names":"Name","type":"participation","type_row_data":"##UserFirstName##","width":25}]},{"name":"Second example","empty_rows_top":0,"empty_rows_bottom":0,"row_minheight_header":20,"row_minheight_body":20,"columns":[{"name":"Name","header_display_names":"Name","type":"participation","type_row_data":"##UserFirstName##","width":25}]}]}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Layout.php","line":500,"reason":"postLayout chooses segment4 Structure and ignores segment3; Vue uses literal post.","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","method":"postLayout"},"x-edoobox-action-contract":{"review":"layout-table-post","status":"source-reviewed-partial","sources":[{"file":"application/models/v2/Layout.php","sha256":"d5bf4faf862da921710d9d2f553b14778a2b78eb8281a2f195dd5d52f9886eaf","methods":["__construct","setStructure_Table","postLayout","updateLayout"]},{"file":"application/models/v2/API_DATA.php","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","methods":["return_insert_data","return_update_data"]},{"file":"application/models/v2/Layoutfunc.php","sha256":"04d3a25db87fd8c8e5f6fd8b6ec9cb899bf3d0bafcc7f286b76381c9e28afdc1","methods":["_before_addTable","_update_table_columns","validateColumns"]}],"preconditions":["Only the selected account owns or changes the layout; placeholder and template expansion use native account context."],"errors":[{"error":"ED80100","meaning":"Required create fields, rejected getter value, or native write validation failed."},{"error":"ED80023","meaning":"Generic batch create exceeds 100 records."},{"error":"ED80100","error_code":"columns_structure_error"}],"unresolved":["Template placeholders, account data, signed ID integrity and rendering output remain native. Scalar getter coercion is documented without inventing rejecting wire limits."]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Layout.php","method":"postLayout","line":496},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"layout","short":"lyot","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipperiod Abrechnungsperioden einer Mitgliedschaft lesen Liest Abrechnungsperioden genau einer Mitgliedschaft. Ohne id muss membership direkt als Query oder als filter-Eintrag vorhanden sein; fehlt beides, folgt membership_filter_required. Ein id-Selector liest stattdessen einen Einzelbeleg. Vor der Rückgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Das kann Perioden/Zahlungsreferenzen, Mitgliedschaftsstatus, Gültigkeit, Nutzungsbestand, Audit und Outbox dauerhaft ändern. Sortierung: period_start absteigend, danach Beleg-ID; total ist die Gesamtzahl vor Pagination. - Operation-ID: get_v2_membershipperiod - Dokumentation: /docs/api/v2/operations/get-v2-membershipperiod-e4c576212d07/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert. | | limit | query | nein | JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt. | | id | query | nein | Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter. | | membership | query | nein | Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"membership","value":"usermembership_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipperiod_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"membership","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"description":"Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipperiod' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipperiod"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipperiod" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipperiod","summary":"Abrechnungsperioden einer Mitgliedschaft lesen","description":"Liest Abrechnungsperioden genau einer Mitgliedschaft. Ohne id muss membership direkt als Query oder als filter-Eintrag vorhanden sein; fehlt beides, folgt membership_filter_required. Ein id-Selector liest stattdessen einen Einzelbeleg. Vor der Rückgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Das kann Perioden/Zahlungsreferenzen, Mitgliedschaftsstatus, Gültigkeit, Nutzungsbestand, Audit und Outbox dauerhaft ändern. Sortierung: period_start absteigend, danach Beleg-ID; total ist die Gesamtzahl vor Pagination.","tags":["membershipperiod"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"membership","value":"usermembership_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipperiod_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"membership","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"description":"Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data ist bei membership-Auswahl eine JSON-Liste (auch leer), bei id-Auswahl ein einzelnes Objekt. error=false und success=true. Zusätzlich total vor Pagination.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["membership"]},{"query":["id"]},{"query":["filter"],"containsProperty":["membership","id"]}],"x-edoobox-source":{"file":"application/models/v2/Membershipperiod.php","method":"getMembershipperiod","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipperiod","short":"mepe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipperiod/{id} Periodenbeleg lesen Liest einen Periodenbeleg anhand der öffentlichen Pfad-ID. Vor der Rückgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Das kann Perioden/Zahlungsreferenzen, Mitgliedschaftsstatus, Gültigkeit, Nutzungsbestand, Audit und Outbox dauerhaft ändern. - Operation-ID: get_v2_membershipperiod_id - Dokumentation: /docs/api/v2/operations/get-v2-membershipperiod-id-902b1b664247/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipperiod_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipperiod/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipperiod/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipperiod/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipperiod_id","summary":"Periodenbeleg lesen","description":"Liest einen Periodenbeleg anhand der öffentlichen Pfad-ID. Vor der Rückgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Das kann Perioden/Zahlungsreferenzen, Mitgliedschaftsstatus, Gültigkeit, Nutzungsbestand, Audit und Outbox dauerhaft ändern.","tags":["membershipperiod"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipperiod_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipperiod.php","method":"getMembershipperiod","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipperiod","short":"mepe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipperiod/list Abrechnungsperioden einer Mitgliedschaft lesen Liest Abrechnungsperioden genau einer Mitgliedschaft. Ohne id muss membership direkt als Query oder als filter-Eintrag vorhanden sein; fehlt beides, folgt membership_filter_required. Ein id-Selector liest stattdessen einen Einzelbeleg. Vor der Rückgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Das kann Perioden/Zahlungsreferenzen, Mitgliedschaftsstatus, Gültigkeit, Nutzungsbestand, Audit und Outbox dauerhaft ändern. Sortierung: period_start absteigend, danach Beleg-ID; total ist die Gesamtzahl vor Pagination. - Operation-ID: get_v2_membershipperiod_list - Dokumentation: /docs/api/v2/operations/get-v2-membershipperiod-list-4f2c6605f7de/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert. | | limit | query | nein | JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt. | | id | query | nein | Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter. | | membership | query | nein | Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"membership","value":"usermembership_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipperiod_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"membership","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"description":"Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipperiod/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipperiod/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipperiod/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipperiod_list","summary":"Abrechnungsperioden einer Mitgliedschaft lesen","description":"Liest Abrechnungsperioden genau einer Mitgliedschaft. Ohne id muss membership direkt als Query oder als filter-Eintrag vorhanden sein; fehlt beides, folgt membership_filter_required. Ein id-Selector liest stattdessen einen Einzelbeleg. Vor der Rückgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Das kann Perioden/Zahlungsreferenzen, Mitgliedschaftsstatus, Gültigkeit, Nutzungsbestand, Audit und Outbox dauerhaft ändern. Sortierung: period_start absteigend, danach Beleg-ID; total ist die Gesamtzahl vor Pagination.","tags":["membershipperiod"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"membership","value":"usermembership_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipperiod_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"membership","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"description":"Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data ist bei membership-Auswahl eine JSON-Liste (auch leer), bei id-Auswahl ein einzelnes Objekt. error=false und success=true. Zusätzlich total vor Pagination.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/5","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Membershipperiod.php","line":21,"reason":"Period-id query uses service.get; normal collection requires a valid tenant-scoped membership filter before listForMembershipPage."},"reason":"List membership periods after reconciling their already-persisted payment state.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/5","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["membership"]},{"query":["id"]},{"query":["filter"],"containsProperty":["membership","id"]}],"x-edoobox-source":{"file":"application/models/v2/Membershipperiod.php","method":"getMembershipperiod","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipperiod","short":"mepe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/membershipplan Mitgliedschaftsplan archivieren Archiviert den Plan (Status archived), ohne ihn physisch zu löschen. Bestehende Mitgliedschaften behalten ihre Snapshots. version muss dem letzten Stand entsprechen; Grund und request_id sind optionale Auditangaben. Auf der Collection-Route muss id entweder als Query oder als oberstes Feld des JSON-Envelopes vorhanden sein. - Operation-ID: delete_v2_membershipplan - Dokumentation: /docs/api/v2/operations/delete-v2-membershipplan-6e09be122bc7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Erforderlicher Objektselector als Query oder oberstes JSON-Feld id; alternative Pfadroute mit {id} verfügbar. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto.","examples":["membershipplan_012345abcdef_123456"]},"description":"Erforderlicher Objektselector als Query oder oberstes JSON-Feld id; alternative Pfadroute mit {id} verfügbar."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "reason": "Plan wird nicht mehr angeboten", "request_id": "demo-plan-archive" }, "id": "membershipplan_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/membershipplan' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "reason": "Plan wird nicht mehr angeboten", "request_id": "demo-plan-archive" }, "id": "membershipplan_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"reason\":\"Plan wird nicht mehr angeboten\",\"request_id\":\"demo-plan-archive\"},\"id\":\"membershipplan_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_membershipplan","summary":"Mitgliedschaftsplan archivieren","description":"Archiviert den Plan (Status archived), ohne ihn physisch zu löschen. Bestehende Mitgliedschaften behalten ihre Snapshots. version muss dem letzten Stand entsprechen; Grund und request_id sind optionale Auditangaben. Auf der Collection-Route muss id entweder als Query oder als oberstes Feld des JSON-Envelopes vorhanden sein.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem aktuellen Konto.","examples":["membershipplan_012345abcdef_123456"]},"description":"Erforderlicher Objektselector als Query oder oberstes JSON-Feld id; alternative Pfadroute mit {id} verfügbar."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":2,"status":"archived","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":1790000000,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string"},"request_id":{"type":"string"}},"additionalProperties":true,"required":["version"]},"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mitgliedschaftsplan archivieren","value":{"data":{"version":1,"reason":"Plan wird nicht mehr angeboten","request_id":"demo-plan-archive"},"id":"membershipplan_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["id"]},{"body":["id"]}],"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"deleteMembershipplan","line":97},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipplan Mitgliedschaftspläne suchen Liest Pläne mit Preisen, Rabatten, Geltungsbereich und aktiver Kundenzahl. JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt. Pagination per limit, total vor Pagination. fields und response_type ändern die feste Ausgabe nicht. - Operation-ID: get_v2_membershipplan - Dokumentation: /docs/api/v2/operations/get-v2-membershipplan-72db4f3201a8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt. | | limit | query | nein | JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt. | | order | query | nein | Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge. | | language | query | nein | Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache. | | id | query | nein | Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"status","value":["published","internal"]}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"},"direction":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"name","value":"ASC"}]}}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipplan_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipplan' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipplan","summary":"Mitgliedschaftspläne suchen","description":"Liest Pläne mit Preisen, Rabatten, Geltungsbereich und aktiver Kundenzahl. JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt. Pagination per limit, total vor Pagination. fields und response_type ändern die feste Ausgabe nicht.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"status","value":["published","internal"]}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"},"direction":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"name","value":"ASC"}]}}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipplan_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"membershipplan_012345abcdef_123456","version":1,"status":"draft","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at"]}},{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"getMembershipplan","line":23},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/membershipplan Mitgliedschaftsplan als Entwurf erstellen Erstellt einen Plan im Status draft mit Version 1. Fehlende Konfiguration nutzt Standards (ein Jahr, kein Rabatt, kostenlos, keine Verlängerung). Texte und Geltungsbereich dürfen im Entwurf fehlen. internal/published benötigen später insbesondere Kontosprachenname, Geltungsbereich und eigene Mehrwertsteuer; Veröffentlichung zusätzlich erfolgreichen Preflight. request_id dient der Audit-Korrelation. Keine Kaufzuweisung. - Operation-ID: post_v2_membershipplan - Dokumentation: /docs/api/v2/operations/post-v2-membershipplan-fc2811bf7d6d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "texts": { "de": { "name": "Jahresmitgliedschaft", "description_html": "

Vorteile für ein Jahr

" } }, "validity": { "type": "relative_duration", "value": 1, "unit": "year" }, "sale": { "amount": "120.00", "currency": "CHF" }, "discount": { "type": "percent", "value": "10.00" }, "scopes": [ { "type": "root" } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/membershipplan' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "texts": { "de": { "name": "Jahresmitgliedschaft", "description_html": "

Vorteile für ein Jahr

" } }, "validity": { "type": "relative_duration", "value": 1, "unit": "year" }, "sale": { "amount": "120.00", "currency": "CHF" }, "discount": { "type": "percent", "value": "10.00" }, "scopes": [ { "type": "root" } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"texts\":{\"de\":{\"name\":\"Jahresmitgliedschaft\",\"description_html\":\"

Vorteile für ein Jahr

\"}},\"validity\":{\"type\":\"relative_duration\",\"value\":1,\"unit\":\"year\"},\"sale\":{\"amount\":\"120.00\",\"currency\":\"CHF\"},\"discount\":{\"type\":\"percent\",\"value\":\"10.00\"},\"scopes\":[{\"type\":\"root\"}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_membershipplan","summary":"Mitgliedschaftsplan als Entwurf erstellen","description":"Erstellt einen Plan im Status draft mit Version 1. Fehlende Konfiguration nutzt Standards (ein Jahr, kein Rabatt, kostenlos, keine Verlängerung). Texte und Geltungsbereich dürfen im Entwurf fehlen. internal/published benötigen später insbesondere Kontosprachenname, Geltungsbereich und eigene Mehrwertsteuer; Veröffentlichung zusätzlich erfolgreichen Preflight. request_id dient der Audit-Korrelation. Keine Kaufzuweisung.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":1,"status":"draft","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"],"default":"relative_duration"},"from":{"type":["integer","number","string","null"],"description":"Unix-Sekunden oder strtotime-lesbarer Datumsstring; feste Gültigkeit benötigt from < until."},"until":{"type":["integer","number","string","null"]},"value":{"type":["integer","number","string","boolean"],"description":"Positive Dauer für relative_duration; Standard 1."},"unit":{"type":["string","null"],"enum":["day","month","year",null],"default":"year"},"uses":{"type":["integer","number","string","boolean"],"description":"Positive Anzahl für usage_count."},"promo_code_redemption":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"],"default":"percent"},"value":{"type":["string","number"],"description":"Nichtnegativer Betrag, Punkt oder Komma; maximal vier Eingabenachkommastellen, auf zwei gerundet. Bei percent höchstens 100."},"currency":{"type":"string","description":"Dreistelliger ISO-Code für fixed; bei percent verworfen."}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":["string","number"],"description":"Nichtnegativ, Punkt oder Komma, maximal vier Nachkommastellen, auf zwei gerundet; Standard 0."},"currency":{"type":"string","description":"Dreistelliger Währungscode, auf Großbuchstaben normalisiert; Standard Kontowährung."},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"default":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"reminder_days":{"type":["integer","number","string","boolean"],"description":"Wird auf 0–365 begrenzt, Standard 30."},"duration_value":{"type":["integer","number","string","boolean"],"description":"Positive Verlängerungsdauer, insbesondere für fixed_range."},"duration_unit":{"type":["string","null"],"enum":["day","month","year",null]}},"additionalProperties":true},"texts":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":150},"description_html":{"type":"string","description":"Wird HTML-bereinigt."}},"additionalProperties":true},"description":"Sprachcode → Textobjekt. Codes werden klein geschrieben; Name der Kontostandardsprache ist erst für internal/published erforderlich."},"scopes":{"type":["array","object"],"items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]},"additionalProperties":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"allow_parallel_instances":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"sort_order":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"internal_reference":{"type":"string","description":"Gekürzt auf 100 Zeichen."},"request_id":{"type":"string","description":"Optionale Audit-Korrelation; für Pläne keine Idempotenzgarantie."},"validity_type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"],"default":"relative_duration","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"fixed_valid_from":{"type":["integer","number","string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"fixed_valid_until":{"type":["integer","number","string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"duration_value":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"duration_unit":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"included_uses":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"promo_code_redemption_enabled":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_type":{"type":"string","enum":["percent","fixed"],"default":"percent","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_value":{"type":["string","number"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_currency_iso":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"sale_price":{"type":["string","number"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"sale_currency_iso":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"auto_renew_allowed":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"auto_renew_default":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"reminder_days":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"renewal_duration_value":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"renewal_duration_unit":{"type":["string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."}},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Mitgliedschaftsplan als Entwurf erstellen","value":{"data":{"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

"}},"validity":{"type":"relative_duration","value":1,"unit":"year"},"sale":{"amount":"120.00","currency":"CHF"},"discount":{"type":"percent","value":"10.00"},"scopes":[{"type":"root"}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"postMembershipplan","line":61},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/membershipplan Mitgliedschaftsplan aktualisieren Aktualisiert die angegebenen Plandaten unter optimistischer Sperre (version aus letzter Antwort). Fehlende Werte bleiben erhalten; übergebene texts/scopes ersetzen ihren ganzen Bereich. Änderungen erhöhen version, bestehende Kaufsnapshots bleiben erhalten. Für veröffentlichte Pläne wird die Publikationsreife erneut geprüft. data.action kann alternativ publish, unpublish oder internal auslösen. Auf der Collection-Route ist id als oberstes Feld des JSON-Envelopes erforderlich; PUT übernimmt keine Querydaten. - Operation-ID: put_v2_membershipplan - Dokumentation: /docs/api/v2/operations/put-v2-membershipplan-93fa91761b50/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "texts": { "de": { "name": "Jahresmitgliedschaft Plus" } } }, "id": "membershipplan_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/membershipplan' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "texts": { "de": { "name": "Jahresmitgliedschaft Plus" } } }, "id": "membershipplan_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"texts\":{\"de\":{\"name\":\"Jahresmitgliedschaft Plus\"}}},\"id\":\"membershipplan_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_membershipplan","summary":"Mitgliedschaftsplan aktualisieren","description":"Aktualisiert die angegebenen Plandaten unter optimistischer Sperre (version aus letzter Antwort). Fehlende Werte bleiben erhalten; übergebene texts/scopes ersetzen ihren ganzen Bereich. Änderungen erhöhen version, bestehende Kaufsnapshots bleiben erhalten. Für veröffentlichte Pläne wird die Publikationsreife erneut geprüft. data.action kann alternativ publish, unpublish oder internal auslösen. Auf der Collection-Route ist id als oberstes Feld des JSON-Envelopes erforderlich; PUT übernimmt keine Querydaten.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":2,"status":"draft","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"],"default":"relative_duration"},"from":{"type":["integer","number","string","null"],"description":"Unix-Sekunden oder strtotime-lesbarer Datumsstring; feste Gültigkeit benötigt from < until."},"until":{"type":["integer","number","string","null"]},"value":{"type":["integer","number","string","boolean"],"description":"Positive Dauer für relative_duration; Standard 1."},"unit":{"type":["string","null"],"enum":["day","month","year",null],"default":"year"},"uses":{"type":["integer","number","string","boolean"],"description":"Positive Anzahl für usage_count."},"promo_code_redemption":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"],"default":"percent"},"value":{"type":["string","number"],"description":"Nichtnegativer Betrag, Punkt oder Komma; maximal vier Eingabenachkommastellen, auf zwei gerundet. Bei percent höchstens 100."},"currency":{"type":"string","description":"Dreistelliger ISO-Code für fixed; bei percent verworfen."}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":["string","number"],"description":"Nichtnegativ, Punkt oder Komma, maximal vier Nachkommastellen, auf zwei gerundet; Standard 0."},"currency":{"type":"string","description":"Dreistelliger Währungscode, auf Großbuchstaben normalisiert; Standard Kontowährung."},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"default":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"reminder_days":{"type":["integer","number","string","boolean"],"description":"Wird auf 0–365 begrenzt, Standard 30."},"duration_value":{"type":["integer","number","string","boolean"],"description":"Positive Verlängerungsdauer, insbesondere für fixed_range."},"duration_unit":{"type":["string","null"],"enum":["day","month","year",null]}},"additionalProperties":true},"texts":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":150},"description_html":{"type":"string","description":"Wird HTML-bereinigt."}},"additionalProperties":true},"description":"Sprachcode → Textobjekt. Codes werden klein geschrieben; Name der Kontostandardsprache ist erst für internal/published erforderlich."},"scopes":{"type":["array","object"],"items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]},"additionalProperties":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"allow_parallel_instances":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"sort_order":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"internal_reference":{"type":"string","description":"Gekürzt auf 100 Zeichen."},"request_id":{"type":"string","description":"Optionale Audit-Korrelation; für Pläne keine Idempotenzgarantie."},"validity_type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"],"default":"relative_duration","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"fixed_valid_from":{"type":["integer","number","string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"fixed_valid_until":{"type":["integer","number","string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"duration_value":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"duration_unit":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"included_uses":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"promo_code_redemption_enabled":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_type":{"type":"string","enum":["percent","fixed"],"default":"percent","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_value":{"type":["string","number"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_currency_iso":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"sale_price":{"type":["string","number"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"sale_currency_iso":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"auto_renew_allowed":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"auto_renew_default":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"reminder_days":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"renewal_duration_value":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"renewal_duration_unit":{"type":["string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"action":{"type":"string","description":"publish/unpublish/internal führen einen Statuswechsel aus; sonst normale Aktualisierung."}},"additionalProperties":true,"required":["version"]},"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Mitgliedschaftsplan aktualisieren","value":{"data":{"version":1,"texts":{"de":{"name":"Jahresmitgliedschaft Plus"}}},"id":"membershipplan_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"updateMembershipplan","line":74},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/membershipplan/{id} Mitgliedschaftsplan archivieren Archiviert den Plan (Status archived), ohne ihn physisch zu löschen. Bestehende Mitgliedschaften behalten ihre Snapshots. version muss dem letzten Stand entsprechen; Grund und request_id sind optionale Auditangaben. - Operation-ID: delete_v2_membershipplan_id - Dokumentation: /docs/api/v2/operations/delete-v2-membershipplan-id-4d73239c30bc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "reason": "Plan wird nicht mehr angeboten", "request_id": "demo-plan-archive" } } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "reason": "Plan wird nicht mehr angeboten", "request_id": "demo-plan-archive" } }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"reason\":\"Plan wird nicht mehr angeboten\",\"request_id\":\"demo-plan-archive\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_membershipplan_id","summary":"Mitgliedschaftsplan archivieren","description":"Archiviert den Plan (Status archived), ohne ihn physisch zu löschen. Bestehende Mitgliedschaften behalten ihre Snapshots. version muss dem letzten Stand entsprechen; Grund und request_id sind optionale Auditangaben.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":2,"status":"archived","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":1790000000,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string"},"request_id":{"type":"string"}},"additionalProperties":true,"required":["version"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mitgliedschaftsplan archivieren","value":{"data":{"version":1,"reason":"Plan wird nicht mehr angeboten","request_id":"demo-plan-archive"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"deleteMembershipplan","line":97},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipplan/{id} Mitgliedschaftsplan im Detail lesen Liest einen konto-eigenen Plan mit aktueller Version, Gültigkeit, Preis, Rabatt, Geltungsbereichen und allen Übersetzungen. language wählt den sichtbaren Namen; fields/response_type werden ignoriert. - Operation-ID: get_v2_membershipplan_id - Dokumentation: /docs/api/v2/operations/get-v2-membershipplan-id-0e6fef2d9ab3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | query | nein | Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipplan_id","summary":"Mitgliedschaftsplan im Detail lesen","description":"Liest einen konto-eigenen Plan mit aktueller Version, Gültigkeit, Preis, Rabatt, Geltungsbereichen und allen Übersetzungen. language wählt den sichtbaren Namen; fields/response_type werden ignoriert.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":1,"status":"draft","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/7","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Membershipplan.php","line":43,"reason":"Item GET calls MembershipPlanService.get after a validated public plan ID."},"reason":"Read one existing membership plan, including stored localized texts and scopes.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/7","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"getMembershipplan","line":23},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/membershipplan/{id} Mitgliedschaftsplan aktualisieren Aktualisiert die angegebenen Plandaten unter optimistischer Sperre (version aus letzter Antwort). Fehlende Werte bleiben erhalten; übergebene texts/scopes ersetzen ihren ganzen Bereich. Änderungen erhöhen version, bestehende Kaufsnapshots bleiben erhalten. Für veröffentlichte Pläne wird die Publikationsreife erneut geprüft. data.action kann alternativ publish, unpublish oder internal auslösen. - Operation-ID: put_v2_membershipplan_id - Dokumentation: /docs/api/v2/operations/put-v2-membershipplan-id-342b776f06e6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "texts": { "de": { "name": "Jahresmitgliedschaft Plus" } } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "texts": { "de": { "name": "Jahresmitgliedschaft Plus" } } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"texts\":{\"de\":{\"name\":\"Jahresmitgliedschaft Plus\"}}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_membershipplan_id","summary":"Mitgliedschaftsplan aktualisieren","description":"Aktualisiert die angegebenen Plandaten unter optimistischer Sperre (version aus letzter Antwort). Fehlende Werte bleiben erhalten; übergebene texts/scopes ersetzen ihren ganzen Bereich. Änderungen erhöhen version, bestehende Kaufsnapshots bleiben erhalten. Für veröffentlichte Pläne wird die Publikationsreife erneut geprüft. data.action kann alternativ publish, unpublish oder internal auslösen.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":2,"status":"draft","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"],"default":"relative_duration"},"from":{"type":["integer","number","string","null"],"description":"Unix-Sekunden oder strtotime-lesbarer Datumsstring; feste Gültigkeit benötigt from < until."},"until":{"type":["integer","number","string","null"]},"value":{"type":["integer","number","string","boolean"],"description":"Positive Dauer für relative_duration; Standard 1."},"unit":{"type":["string","null"],"enum":["day","month","year",null],"default":"year"},"uses":{"type":["integer","number","string","boolean"],"description":"Positive Anzahl für usage_count."},"promo_code_redemption":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"],"default":"percent"},"value":{"type":["string","number"],"description":"Nichtnegativer Betrag, Punkt oder Komma; maximal vier Eingabenachkommastellen, auf zwei gerundet. Bei percent höchstens 100."},"currency":{"type":"string","description":"Dreistelliger ISO-Code für fixed; bei percent verworfen."}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":["string","number"],"description":"Nichtnegativ, Punkt oder Komma, maximal vier Nachkommastellen, auf zwei gerundet; Standard 0."},"currency":{"type":"string","description":"Dreistelliger Währungscode, auf Großbuchstaben normalisiert; Standard Kontowährung."},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"default":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"reminder_days":{"type":["integer","number","string","boolean"],"description":"Wird auf 0–365 begrenzt, Standard 30."},"duration_value":{"type":["integer","number","string","boolean"],"description":"Positive Verlängerungsdauer, insbesondere für fixed_range."},"duration_unit":{"type":["string","null"],"enum":["day","month","year",null]}},"additionalProperties":true},"texts":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{"type":"string","maxLength":150},"description_html":{"type":"string","description":"Wird HTML-bereinigt."}},"additionalProperties":true},"description":"Sprachcode → Textobjekt. Codes werden klein geschrieben; Name der Kontostandardsprache ist erst für internal/published erforderlich."},"scopes":{"type":["array","object"],"items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]},"additionalProperties":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"allow_parallel_instances":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"sort_order":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"internal_reference":{"type":"string","description":"Gekürzt auf 100 Zeichen."},"request_id":{"type":"string","description":"Optionale Audit-Korrelation; für Pläne keine Idempotenzgarantie."},"validity_type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"],"default":"relative_duration","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"fixed_valid_from":{"type":["integer","number","string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"fixed_valid_until":{"type":["integer","number","string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"duration_value":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"duration_unit":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"included_uses":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"promo_code_redemption_enabled":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_type":{"type":"string","enum":["percent","fixed"],"default":"percent","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_value":{"type":["string","number"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"discount_currency_iso":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"sale_price":{"type":["string","number"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"sale_currency_iso":{"type":"string","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"auto_renew_allowed":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"auto_renew_default":{"type":["boolean","integer","string"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"reminder_days":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"renewal_duration_value":{"type":["integer","number","string","boolean"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"renewal_duration_unit":{"type":["string","null"],"description":"Flacher Eingabealias; die entsprechende verschachtelte Eigenschaft hat Vorrang."},"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"action":{"type":"string","description":"publish/unpublish/internal führen einen Statuswechsel aus; sonst normale Aktualisierung."}},"additionalProperties":true,"required":["version"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mitgliedschaftsplan aktualisieren","value":{"data":{"version":1,"texts":{"de":{"name":"Jahresmitgliedschaft Plus"}}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"updateMembershipplan","line":74},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipplan/{id}/dashboard Kennzahlen eines Mitgliedschaftsplans lesen Liest Mitgliedschaftsanzahlen, Umsatz, Statusverteilung, monatliche Käufe der letzten zwölf Monate sowie je bis zu acht letzte Käufe und Buchungen. Zahlungsabgleich vor der Statistik kann Mitgliedschafts-/Periodenstatus, Zahlungsreferenzen, Audit und Outbox verändern. - Operation-ID: get_v2_membershipplan_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-membershipplan-id-dashboard-ba59684a192b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | query | nein | Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache. | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipplan_id_dashboard","summary":"Kennzahlen eines Mitgliedschaftsplans lesen","description":"Liest Mitgliedschaftsanzahlen, Umsatz, Statusverteilung, monatliche Käufe der letzten zwölf Monate sowie je bis zu acht letzte Käufe und Buchungen. Zahlungsabgleich vor der Statistik kann Mitgliedschafts-/Periodenstatus, Zahlungsreferenzen, Audit und Outbox verändern.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"summary":{"total_memberships":0,"active_memberships":0,"scheduled_memberships":0,"purchased_memberships":0,"revenue":"0.00","currency":"","used_bookings":0},"status_distribution":[],"purchases_over_time":[],"latest_purchases":[],"recent_bookings":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"summary":{"type":"object","properties":{"total_memberships":{"type":"integer"},"active_memberships":{"type":"integer"},"scheduled_memberships":{"type":"integer"},"purchased_memberships":{"type":"integer"},"used_bookings":{"type":"integer"},"revenue":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true},"status_distribution":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string"},"count":{"type":"integer"}},"additionalProperties":true,"required":["status","count"]}},"purchases_over_time":{"type":"array","items":{"type":"object","properties":{"period":{"type":"string"},"count":{"type":"integer"},"revenue":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."}},"additionalProperties":true,"required":["period","count","revenue"]}},"latest_purchases":{"type":"array","items":{"type":"object","properties":{"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_name":{"type":"string"},"user_email":{"type":"string"},"status":{"type":"string"},"source":{"type":"string"},"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"created_at":{"type":"integer"}},"additionalProperties":true}},"recent_bookings":{"type":"array","items":{"type":"object","properties":{"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_name":{"type":"string"},"booking":{"type":["string","null"],"pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"offer":{"type":["string","null"],"pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"offer_name":{"type":"string"},"number":{"type":["string","null"]},"amount":{"type":["string","null"]},"currency":{"type":["string","null"]},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"quantity":{"type":"integer"},"created_at":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true,"required":["summary","status_distribution","purchases_over_time","latest_purchases","recent_bookings"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Membershipplan.php","line":36,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/8","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Membershipplan.php","line":36,"reason":"Exact dashboard branch calls MembershipPlanDashboardService.get."},"reason":"Read membership dashboard aggregates after reconciling existing payments for this plan.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/8","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"getMembershipplan","line":23,"branch":{"file":"application/models/v2/Membershipplan.php","line":36}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/membershipplan/{id}/internal Mitgliedschaftsplan intern verwenden Setzt den Status auf internal: administrative Käufe/Zuweisungen sind möglich, öffentliche Veröffentlichung erfolgt dadurch nicht. Prüft mindestens Standardsprache, Steuer und Geltungsbereiche. version muss dem letzten gelesenen Stand entsprechen; der Statuswechsel erhöht die Version und schreibt Audit/Outbox. - Operation-ID: put_v2_membershipplan_id_internal - Dokumentation: /docs/api/v2/operations/put-v2-membershipplan-id-internal-2eded3f904ee/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "request_id": "demo-plan-internal" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/'"${PATH_ID}"'/internal' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/{id}/internal"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "request_id": "demo-plan-internal" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/{id}/internal" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"request_id\":\"demo-plan-internal\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_membershipplan_id_internal","summary":"Mitgliedschaftsplan intern verwenden","description":"Setzt den Status auf internal: administrative Käufe/Zuweisungen sind möglich, öffentliche Veröffentlichung erfolgt dadurch nicht. Prüft mindestens Standardsprache, Steuer und Geltungsbereiche. version muss dem letzten gelesenen Stand entsprechen; der Statuswechsel erhöht die Version und schreibt Audit/Outbox.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":2,"status":"internal","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"request_id":{"type":"string"}},"additionalProperties":true,"required":["version"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mitgliedschaftsplan intern verwenden","value":{"data":{"version":1,"request_id":"demo-plan-internal"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"updateMembershipplan","line":74,"branch":{"file":"application/models/v2/Membershipplan.php","line":87}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/membershipplan/{id}/publish Mitgliedschaftsplan veröffentlichen Setzt den Status auf published; prüft Texte, Steuer, Geltungsbereiche und serverseitigen Veröffentlichungs-Preflight. version muss dem letzten gelesenen Stand entsprechen; der Statuswechsel erhöht die Version und schreibt Audit/Outbox. - Operation-ID: put_v2_membershipplan_id_publish - Dokumentation: /docs/api/v2/operations/put-v2-membershipplan-id-publish-b55b7e9a81c7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "request_id": "demo-plan-publish" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/'"${PATH_ID}"'/publish' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/{id}/publish"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "request_id": "demo-plan-publish" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/{id}/publish" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"request_id\":\"demo-plan-publish\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_membershipplan_id_publish","summary":"Mitgliedschaftsplan veröffentlichen","description":"Setzt den Status auf published; prüft Texte, Steuer, Geltungsbereiche und serverseitigen Veröffentlichungs-Preflight. version muss dem letzten gelesenen Stand entsprechen; der Statuswechsel erhöht die Version und schreibt Audit/Outbox.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":2,"status":"published","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"request_id":{"type":"string"}},"additionalProperties":true,"required":["version"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mitgliedschaftsplan veröffentlichen","value":{"data":{"version":1,"request_id":"demo-plan-publish"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"updateMembershipplan","line":74,"branch":{"file":"application/models/v2/Membershipplan.php","line":85}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipplan/{id}/publish-preflight Veröffentlichungsreife eines Mitgliedschaftsplans prüfen Prüft Konfiguration, Standardsprache/weitere Kontosprachen, Steuer, Geltungsbereiche, Checkout-Design, Formular, Rechtstexte und Teilnehmerkonto-Widget. ready=false mit issues ist ein erfolgreicher Prüflauf. Die Prüfung selbst ändert keine Pläne oder Designs und benötigt canConfigure. - Operation-ID: get_v2_membershipplan_id_publish_preflight - Dokumentation: /docs/api/v2/operations/get-v2-membershipplan-id-publish-preflight-87a041d1d877/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | query | nein | Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache. | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/'"${PATH_ID}"'/publish-preflight' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/{id}/publish-preflight"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/{id}/publish-preflight" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipplan_id_publish_preflight","summary":"Veröffentlichungsreife eines Mitgliedschaftsplans prüfen","description":"Prüft Konfiguration, Standardsprache/weitere Kontosprachen, Steuer, Geltungsbereiche, Checkout-Design, Formular, Rechtstexte und Teilnehmerkonto-Widget. ready=false mit issues ist ein erfolgreicher Prüflauf. Die Prüfung selbst ändert keine Pläne oder Designs und benötigt canConfigure.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"data enthält bei Planauswahl das Preflight-Objekt (ready, issues, design, settings, languages). Auf /list ohne id-Auswahl stattdessen normale Planliste mit total. error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":1,"ready":false,"issues":[{"key":"design","code":"membership_design_not_found","severity":"error"}],"design":{"id":null,"resolved":null,"customer":null,"form_configured":false,"legal_texts_active":false,"customer_widget_present":false,"can_add_customer_widget":false},"settings":[],"languages":{"configured":["de"],"default":"de","missing":[],"required":["de"]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"ready":{"type":"boolean"},"issues":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"code":{"type":"string"},"severity":{"type":"string","enum":["error","warning"]}},"additionalProperties":true,"required":["key","code","severity"]}},"design":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"resolved":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"customer":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"form_configured":{"type":"boolean"},"legal_texts_active":{"type":"boolean"},"customer_widget_present":{"type":"boolean"},"can_add_customer_widget":{"type":"boolean"}},"additionalProperties":true},"settings":{"oneOf":[{"type":"object","properties":{"form":{"type":"object","properties":{"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"template":{"type":"string"},"widget":{"type":"string"}},"additionalProperties":true},"legal":{"type":"object","properties":{"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"template":{"type":"string"},"widget":{"type":"string"}},"additionalProperties":true},"customer_widget":{"type":["object","null"],"properties":{"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"template":{"type":"string"},"widget":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true},{"type":"array","maxItems":0}]},"languages":{"type":"object","properties":{"configured":{"type":"array","items":{"type":"string"}},"default":{"type":"string"},"missing":{"type":"array","items":{"type":"string"}},"required":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"additionalProperties":true,"required":["id","version","ready","issues","design","settings","languages"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Membershipplan.php","line":33,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/9","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Membershipplan.php","line":33,"reason":"Exact publish-preflight GET calls forPlan only, not publish/addCustomerWidget."},"reason":"Compute publication readiness from existing plan, design, translations, widget configuration and fixed legal texts.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/9","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"getMembershipplan","line":23,"branch":{"file":"application/models/v2/Membershipplan.php","line":33}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/membershipplan/{id}/unpublish Mitgliedschaftsplan zurückziehen Setzt den Status auf draft; bestehende Mitgliedschaften und ihre Kaufsnapshots bleiben erhalten. version muss dem letzten gelesenen Stand entsprechen; der Statuswechsel erhöht die Version und schreibt Audit/Outbox. - Operation-ID: put_v2_membershipplan_id_unpublish - Dokumentation: /docs/api/v2/operations/put-v2-membershipplan-id-unpublish-8f1561f7f123/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "request_id": "demo-plan-unpublish" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/'"${PATH_ID}"'/unpublish' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/{id}/unpublish"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "request_id": "demo-plan-unpublish" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/{id}/unpublish" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"request_id\":\"demo-plan-unpublish\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_membershipplan_id_unpublish","summary":"Mitgliedschaftsplan zurückziehen","description":"Setzt den Status auf draft; bestehende Mitgliedschaften und ihre Kaufsnapshots bleiben erhalten. version muss dem letzten gelesenen Stand entsprechen; der Statuswechsel erhöht die Version und schreibt Audit/Outbox.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipplan_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":2,"status":"draft","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000,"texts":{"de":{"name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","content_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"request_id":{"type":"string"}},"additionalProperties":true,"required":["version"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mitgliedschaftsplan zurückziehen","value":{"data":{"version":1,"request_id":"demo-plan-unpublish"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"updateMembershipplan","line":74,"branch":{"file":"application/models/v2/Membershipplan.php","line":86}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipplan/list Mitgliedschaftspläne suchen Liest Pläne mit Preisen, Rabatten, Geltungsbereich und aktiver Kundenzahl. JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt. Pagination per limit, total vor Pagination. fields und response_type ändern die feste Ausgabe nicht. - Operation-ID: get_v2_membershipplan_list - Dokumentation: /docs/api/v2/operations/get-v2-membershipplan-list-b28bc532e88b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt. | | limit | query | nein | JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt. | | order | query | nein | Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge. | | language | query | nein | Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache. | | id | query | nein | Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"status","value":["published","internal"]}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"},"direction":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"name","value":"ASC"}]}}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipplan_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipplan_list","summary":"Mitgliedschaftspläne suchen","description":"Liest Pläne mit Preisen, Rabatten, Geltungsbereich und aktiver Kundenzahl. JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt. Pagination per limit, total vor Pagination. fields und response_type ändern die feste Ausgabe nicht.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-Filter mit property/value; AND zwischen Eigenschaften, OR zwischen Enum-Werten. status (draft/internal/published/archived), validity_type, duration_unit, discount_type, scope_type akzeptieren String oder Stringliste. Weitere Felder: assignable, name, name_match (contains/starts_with/equals), search (Name/Beschreibung), sale_currency_iso, auto_renew_allowed (0/1/bool), valid_from/valid_until (Überlappung nur fixed_range). Grenzen *_min/*_max für sale_price, discount_value, included_uses, duration_value, active_customer_count. Numerische Grenzen akzeptieren Zahl oder numerischen String; ungültige Werte ergeben membership_invalid_filter. id wählt statt einer Liste ein Einzelobjekt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"status","value":["published","internal"]}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit start (Offset) und reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; negative Offsets auf 0 gesetzt.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"},"direction":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"name","value":"ASC"}]}}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipplan_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"membershipplan_012345abcdef_123456","version":1,"status":"draft","name":"Jahresmitgliedschaft","description_html":"

Vorteile für ein Jahr

","language":"de","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null},"publication":{"design":null},"renewal":{"allowed":false,"default":false,"reminder_days":30,"duration_value":null,"duration_unit":null},"allow_parallel_instances":false,"active_customer_count":0,"sort_order":0,"internal_reference":"Mitgliedschaft 2027","scopes":[{"type":"root","resource":null}],"published_at":null,"archived_at":null,"created_at":1790000000,"updated_at":1790000000}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at"]}},{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"texts":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"description_html":{"type":"string"},"content_hash":{"type":"string"}},"additionalProperties":true},"items":{},"description":"Sprachcode → Textobjekt; ohne Übersetzungen kann PHP [] liefern."}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at","texts"]}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/6","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Membershipplan.php","line":45,"reason":"GET collection delegates filters/order/pagination to service.list."},"reason":"List existing membership plans and computed current customer counts.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/6","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"getMembershipplan","line":23},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipplan/list/dashboard Kennzahlen eines Mitgliedschaftsplans lesen Liest Mitgliedschaftsanzahlen, Umsatz, Statusverteilung, monatliche Käufe der letzten zwölf Monate sowie je bis zu acht letzte Käufe und Buchungen. Zahlungsabgleich vor der Statistik kann Mitgliedschafts-/Periodenstatus, Zahlungsreferenzen, Audit und Outbox verändern. Auf der /list-Variante muss id als Query oder filter-Auswahl vorhanden sein; andernfalls wird die gewöhnliche Planliste mit total ausgegeben. Beim Listenfallback gelten dieselben Filter und order-Felder wie bei /v2/membershipplan/list. - Operation-ID: get_v2_membershipplan_list_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-membershipplan-list-dashboard-f977ffe094ea/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | id muss einen einzelnen Plan auswählen. Ohne id-Selector wird trotz Aktionssuffix die normale Planliste ausgegeben. | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0. | | language | query | nein | Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache. | | id | query | nein | Plan für diese Aktion auswählen; alternativ filter-Eintrag id. | | order | query | nein | Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"id muss einen einzelnen Plan auswählen. Ohne id-Selector wird trotz Aktionssuffix die normale Planliste ausgegeben.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"membershipplan_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["membershipplan_012345abcdef_123456"]},"description":"Plan für diese Aktion auswählen; alternativ filter-Eintrag id."},{"name":"order","in":"query","required":false,"content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"},"direction":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"name","value":"ASC"}]}},"description":"Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/list/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/list/dashboard"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/list/dashboard" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipplan_list_dashboard","summary":"Kennzahlen eines Mitgliedschaftsplans lesen","description":"Liest Mitgliedschaftsanzahlen, Umsatz, Statusverteilung, monatliche Käufe der letzten zwölf Monate sowie je bis zu acht letzte Käufe und Buchungen. Zahlungsabgleich vor der Statistik kann Mitgliedschafts-/Periodenstatus, Zahlungsreferenzen, Audit und Outbox verändern. Auf der /list-Variante muss id als Query oder filter-Auswahl vorhanden sein; andernfalls wird die gewöhnliche Planliste mit total ausgegeben. Beim Listenfallback gelten dieselben Filter und order-Felder wie bei /v2/membershipplan/list.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"id muss einen einzelnen Plan auswählen. Ohne id-Selector wird trotz Aktionssuffix die normale Planliste ausgegeben.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"membershipplan_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["membershipplan_012345abcdef_123456"]},"description":"Plan für diese Aktion auswählen; alternativ filter-Eintrag id."},{"name":"order","in":"query","required":false,"content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"},"direction":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"name","value":"ASC"}]}},"description":"Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"summary":{"total_memberships":0,"active_memberships":0,"scheduled_memberships":0,"purchased_memberships":0,"revenue":"0.00","currency":"","used_bookings":0},"status_distribution":[],"purchases_over_time":[],"latest_purchases":[],"recent_bookings":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","properties":{"summary":{"type":"object","properties":{"total_memberships":{"type":"integer"},"active_memberships":{"type":"integer"},"scheduled_memberships":{"type":"integer"},"purchased_memberships":{"type":"integer"},"used_bookings":{"type":"integer"},"revenue":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true},"status_distribution":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string"},"count":{"type":"integer"}},"additionalProperties":true,"required":["status","count"]}},"purchases_over_time":{"type":"array","items":{"type":"object","properties":{"period":{"type":"string"},"count":{"type":"integer"},"revenue":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."}},"additionalProperties":true,"required":["period","count","revenue"]}},"latest_purchases":{"type":"array","items":{"type":"object","properties":{"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_name":{"type":"string"},"user_email":{"type":"string"},"status":{"type":"string"},"source":{"type":"string"},"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"created_at":{"type":"integer"}},"additionalProperties":true}},"recent_bookings":{"type":"array","items":{"type":"object","properties":{"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_name":{"type":"string"},"booking":{"type":["string","null"],"pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"offer":{"type":["string","null"],"pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"offer_name":{"type":"string"},"number":{"type":["string","null"]},"amount":{"type":["string","null"]},"currency":{"type":["string","null"]},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"quantity":{"type":"integer"},"created_at":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true,"required":["summary","status_distribution","purchases_over_time","latest_purchases","recent_bookings"]},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at"]}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["id"],"effect":"selected action"},{"query":["filter"],"containsProperty":["id"],"effect":"selected action"},{"query":[],"effect":"ordinary collection"}],"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"getMembershipplan","line":23,"branch":{"file":"application/models/v2/Membershipplan.php","line":36}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipplan/list/publish-preflight Veröffentlichungsreife eines Mitgliedschaftsplans prüfen Prüft Konfiguration, Standardsprache/weitere Kontosprachen, Steuer, Geltungsbereiche, Checkout-Design, Formular, Rechtstexte und Teilnehmerkonto-Widget. ready=false mit issues ist ein erfolgreicher Prüflauf. Die Prüfung selbst ändert keine Pläne oder Designs und benötigt canConfigure. Auf der /list-Variante muss id als Query oder filter-Auswahl vorhanden sein; andernfalls wird die gewöhnliche Planliste mit total ausgegeben. Beim Listenfallback gelten dieselben Filter und order-Felder wie bei /v2/membershipplan/list. - Operation-ID: get_v2_membershipplan_list_publish_preflight - Dokumentation: /docs/api/v2/operations/get-v2-membershipplan-list-publish-preflight-b3284d70e117/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | id muss einen einzelnen Plan auswählen. Ohne id-Selector wird trotz Aktionssuffix die normale Planliste ausgegeben. | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0. | | language | query | nein | Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache. | | id | query | nein | Plan für diese Aktion auswählen; alternativ filter-Eintrag id. | | order | query | nein | Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"id muss einen einzelnen Plan auswählen. Ohne id-Selector wird trotz Aktionssuffix die normale Planliste ausgegeben.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"membershipplan_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["membershipplan_012345abcdef_123456"]},"description":"Plan für diese Aktion auswählen; alternativ filter-Eintrag id."},{"name":"order","in":"query","required":false,"content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"},"direction":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"name","value":"ASC"}]}},"description":"Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipplan/list/publish-preflight' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipplan/list/publish-preflight"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipplan/list/publish-preflight" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipplan_list_publish_preflight","summary":"Veröffentlichungsreife eines Mitgliedschaftsplans prüfen","description":"Prüft Konfiguration, Standardsprache/weitere Kontosprachen, Steuer, Geltungsbereiche, Checkout-Design, Formular, Rechtstexte und Teilnehmerkonto-Widget. ready=false mit issues ist ein erfolgreicher Prüflauf. Die Prüfung selbst ändert keine Pläne oder Designs und benötigt canConfigure. Auf der /list-Variante muss id als Query oder filter-Auswahl vorhanden sein; andernfalls wird die gewöhnliche Planliste mit total ausgegeben. Beim Listenfallback gelten dieselben Filter und order-Felder wie bei /v2/membershipplan/list.","tags":["membershipplan"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"id muss einen einzelnen Plan auswählen. Ohne id-Selector wird trotz Aktionssuffix die normale Planliste ausgegeben.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"membershipplan_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"query","required":false,"description":"Gewünschte Textsprache; Fallback Kontosprache, danach vorhandene Sprache.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["membershipplan_012345abcdef_123456"]},"description":"Plan für diese Aktion auswählen; alternativ filter-Eintrag id."},{"name":"order","in":"query","required":false,"content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"field":{"type":"string"},"value":{"type":"string"},"direction":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"name","value":"ASC"}]}},"description":"Sortierung nach updated_at, created_at, status, validity_type, sale_price, discount_value, sort_order, name oder active_customer_count; value/direction ASC/DESC. Ungültige Klauseln werden ignoriert. Standard sort_order ASC, danach stabile ID-Reihenfolge."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält bei Planauswahl das Preflight-Objekt (ready, issues, design, settings, languages). Auf /list ohne id-Auswahl stattdessen normale Planliste mit total. error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipplan_012345abcdef_123456","version":1,"ready":false,"issues":[{"key":"design","code":"membership_design_not_found","severity":"error"}],"design":{"id":null,"resolved":null,"customer":null,"form_configured":false,"legal_texts_active":false,"customer_widget_present":false,"can_add_customer_widget":false},"settings":[],"languages":{"configured":["de"],"default":"de","missing":[],"required":["de"]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"ready":{"type":"boolean"},"issues":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"code":{"type":"string"},"severity":{"type":"string","enum":["error","warning"]}},"additionalProperties":true,"required":["key","code","severity"]}},"design":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"resolved":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"customer":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"form_configured":{"type":"boolean"},"legal_texts_active":{"type":"boolean"},"customer_widget_present":{"type":"boolean"},"can_add_customer_widget":{"type":"boolean"}},"additionalProperties":true},"settings":{"oneOf":[{"type":"object","properties":{"form":{"type":"object","properties":{"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"template":{"type":"string"},"widget":{"type":"string"}},"additionalProperties":true},"legal":{"type":"object","properties":{"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"template":{"type":"string"},"widget":{"type":"string"}},"additionalProperties":true},"customer_widget":{"type":["object","null"],"properties":{"design":{"type":"string","pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"template":{"type":"string"},"widget":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true},{"type":"array","maxItems":0}]},"languages":{"type":"object","properties":{"configured":{"type":"array","items":{"type":"string"}},"default":{"type":"string"},"missing":{"type":"array","items":{"type":"string"}},"required":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"additionalProperties":true,"required":["id","version","ready","issues","design","settings","languages"]},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"version":{"type":"integer"},"status":{"type":"string","enum":["draft","internal","published","archived"]},"name":{"type":"string"},"description_html":{"type":"string"},"language":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"publication":{"type":"object","properties":{"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","properties":{"allowed":{"type":"boolean"},"default":{"type":"boolean"},"reminder_days":{"type":"integer"},"duration_value":{"type":["integer","null"]},"duration_unit":{"type":["string","null"]}},"additionalProperties":true},"allow_parallel_instances":{"type":"boolean"},"active_customer_count":{"type":"integer"},"sort_order":{"type":"integer"},"internal_reference":{"type":"string"},"scopes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["root","category","offer"]},"resource":{"type":["string","null"],"description":"v2 category-/offer-ID; bei root null."},"id":{"type":"string","description":"Eingabealias für resource."}},"additionalProperties":true,"required":["type"]}},"published_at":{"type":["integer","null"]},"archived_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","version","status","name","description_html","language","validity","discount","sale","publication","renewal","allow_parallel_instances","active_customer_count","sort_order","internal_reference","scopes","published_at","archived_at","created_at","updated_at"]}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["id"],"effect":"selected action"},{"query":["filter"],"containsProperty":["id"],"effect":"selected action"},{"query":[],"effect":"ordinary collection"}],"x-edoobox-source":{"file":"application/models/v2/Membershipplan.php","method":"getMembershipplan","line":23,"branch":{"file":"application/models/v2/Membershipplan.php","line":33}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipplan","short":"mepl","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipredemption Nutzungsbuchungen einer Mitgliedschaft lesen Liest Nutzungsbuchungen genau einer Mitgliedschaft. Ohne id muss membership direkt als Query oder als filter-Eintrag vorhanden sein; fehlt beides, folgt membership_filter_required. Ein id-Selector liest stattdessen einen Einzelbeleg. Liest ausschließlich das unveränderliche Nutzungsjournal; keine Reservierung, Einlösung oder Zustandsänderung. Sortierung: created_at absteigend, danach Beleg-ID. Nur reply wird benutzt (1–500, Standard durch Dispatcher 100); start/Offset wird ignoriert und es gibt kein total. - Operation-ID: get_v2_membershipredemption - Dokumentation: /docs/api/v2/operations/get-v2-membershipredemption-c67ddc8f76bf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert. | | limit | query | nein | JSON-Objekt mit reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; start wird ignoriert. | | id | query | nein | Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter. | | membership | query | nein | Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"membership","value":"usermembership_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; start wird ignoriert.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipredemption_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"membership","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"description":"Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipredemption' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipredemption"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipredemption" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipredemption","summary":"Nutzungsbuchungen einer Mitgliedschaft lesen","description":"Liest Nutzungsbuchungen genau einer Mitgliedschaft. Ohne id muss membership direkt als Query oder als filter-Eintrag vorhanden sein; fehlt beides, folgt membership_filter_required. Ein id-Selector liest stattdessen einen Einzelbeleg. Liest ausschließlich das unveränderliche Nutzungsjournal; keine Reservierung, Einlösung oder Zustandsänderung. Sortierung: created_at absteigend, danach Beleg-ID. Nur reply wird benutzt (1–500, Standard durch Dispatcher 100); start/Offset wird ignoriert und es gibt kein total.","tags":["membershipredemption"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"membership","value":"usermembership_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; start wird ignoriert.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipredemption_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"membership","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"description":"Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data ist bei membership-Auswahl eine JSON-Liste (auch leer), bei id-Auswahl ein einzelnes Objekt. error=false und success=true. Kein total und kein Offset.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"membershipredemption_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","user":"user_012345abcdef_123456","event_type":"consume","quantity_delta":-1,"offer":"offer_012345abcdef_123456","reverses":null,"reason":"Buchung","actor_type":"system","created_at":1790000000}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"event_type":{"type":"string"},"quantity_delta":{"type":"integer"},"offer":{"type":["string","null"],"pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reverses":{"type":["string","null"],"pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reason":{"type":"string"},"actor_type":{"type":"string"},"created_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","user","event_type","quantity_delta","offer","reverses","reason","actor_type","created_at"]}},{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"event_type":{"type":"string"},"quantity_delta":{"type":"integer"},"offer":{"type":["string","null"],"pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reverses":{"type":["string","null"],"pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reason":{"type":"string"},"actor_type":{"type":"string"},"created_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","user","event_type","quantity_delta","offer","reverses","reason","actor_type","created_at"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["membership"]},{"query":["id"]},{"query":["filter"],"containsProperty":["membership","id"]}],"x-edoobox-source":{"file":"application/models/v2/Membershipredemption.php","method":"getMembershipredemption","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipredemption","short":"mere","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipredemption/{id} Nutzungsbeleg lesen Liest einen Nutzungsbeleg anhand der öffentlichen Pfad-ID. Liest ausschließlich das unveränderliche Nutzungsjournal; keine Reservierung, Einlösung oder Zustandsänderung. - Operation-ID: get_v2_membershipredemption_id - Dokumentation: /docs/api/v2/operations/get-v2-membershipredemption-id-45f69ab46a94/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipredemption_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipredemption/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipredemption/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipredemption/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipredemption_id","summary":"Nutzungsbeleg lesen","description":"Liest einen Nutzungsbeleg anhand der öffentlichen Pfad-ID. Liest ausschließlich das unveränderliche Nutzungsjournal; keine Reservierung, Einlösung oder Zustandsänderung.","tags":["membershipredemption"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["membershipredemption_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnisobjekt; error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"membershipredemption_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","user":"user_012345abcdef_123456","event_type":"consume","quantity_delta":-1,"offer":"offer_012345abcdef_123456","reverses":null,"reason":"Buchung","actor_type":"system","created_at":1790000000}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"event_type":{"type":"string"},"quantity_delta":{"type":"integer"},"offer":{"type":["string","null"],"pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reverses":{"type":["string","null"],"pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reason":{"type":"string"},"actor_type":{"type":"string"},"created_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","user","event_type","quantity_delta","offer","reverses","reason","actor_type","created_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Membershipredemption.php","method":"getMembershipredemption","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipredemption","short":"mere","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/membershipredemption/list Nutzungsbuchungen einer Mitgliedschaft lesen Liest Nutzungsbuchungen genau einer Mitgliedschaft. Ohne id muss membership direkt als Query oder als filter-Eintrag vorhanden sein; fehlt beides, folgt membership_filter_required. Ein id-Selector liest stattdessen einen Einzelbeleg. Liest ausschließlich das unveränderliche Nutzungsjournal; keine Reservierung, Einlösung oder Zustandsänderung. Sortierung: created_at absteigend, danach Beleg-ID. Nur reply wird benutzt (1–500, Standard durch Dispatcher 100); start/Offset wird ignoriert und es gibt kein total. - Operation-ID: get_v2_membershipredemption_list - Dokumentation: /docs/api/v2/operations/get-v2-membershipredemption-list-e4511090ab1a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert. | | limit | query | nein | JSON-Objekt mit reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; start wird ignoriert. | | id | query | nein | Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter. | | membership | query | nein | Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"membership","value":"usermembership_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; start wird ignoriert.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipredemption_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"membership","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"description":"Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/membershipredemption/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/membershipredemption/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/membershipredemption/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_membershipredemption_list","summary":"Nutzungsbuchungen einer Mitgliedschaft lesen","description":"Liest Nutzungsbuchungen genau einer Mitgliedschaft. Ohne id muss membership direkt als Query oder als filter-Eintrag vorhanden sein; fehlt beides, folgt membership_filter_required. Ein id-Selector liest stattdessen einen Einzelbeleg. Liest ausschließlich das unveränderliche Nutzungsjournal; keine Reservierung, Einlösung oder Zustandsänderung. Sortierung: created_at absteigend, danach Beleg-ID. Nur reply wird benutzt (1–500, Standard durch Dispatcher 100); start/Offset wird ignoriert und es gibt kein total.","tags":["membershipredemption"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-Filter: id wählt einen Einzelbeleg; membership wählt die zugehörige Mitgliedschaft. Mindestens eine dieser Auswahlen ist erforderlich. Andere Eigenschaften und expression werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Bei diesen Ressourcen ignoriert; Auswahl erfolgt anhand von property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"membership","value":"usermembership_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt mit reply (Seitengröße, 1–500); alternativ eine Zahl. Standard reply=100. Größere Werte werden begrenzt; start wird ignoriert.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID.","examples":["membershipredemption_012345abcdef_123456"]},"description":"Einzelobjekt über id auswählen; alternativ gleichnamiger Eintrag im JSON-filter."},{"name":"membership","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"description":"Pflichtauswahl für die Liste: Mitgliedschaft im aktuellen Konto; alternativ filter mit property=membership."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data ist bei membership-Auswahl eine JSON-Liste (auch leer), bei id-Auswahl ein einzelnes Objekt. error=false und success=true. Kein total und kein Offset.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"membershipredemption_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","user":"user_012345abcdef_123456","event_type":"consume","quantity_delta":-1,"offer":"offer_012345abcdef_123456","reverses":null,"reason":"Buchung","actor_type":"system","created_at":1790000000}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"event_type":{"type":"string"},"quantity_delta":{"type":"integer"},"offer":{"type":["string","null"],"pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reverses":{"type":["string","null"],"pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reason":{"type":"string"},"actor_type":{"type":"string"},"created_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","user","event_type","quantity_delta","offer","reverses","reason","actor_type","created_at"]}},{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"event_type":{"type":"string"},"quantity_delta":{"type":"integer"},"offer":{"type":["string","null"],"pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reverses":{"type":["string","null"],"pattern":"^membershipredemption_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reason":{"type":"string"},"actor_type":{"type":"string"},"created_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","user","event_type","quantity_delta","offer","reverses","reason","actor_type","created_at"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["membership"]},{"query":["id"]},{"query":["filter"],"containsProperty":["membership","id"]}],"x-edoobox-source":{"file":"application/models/v2/Membershipredemption.php","method":"getMembershipredemption","line":17},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"membershipredemption","short":"mere","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/note Notizen auflisten Liest manuelle und Systemnotizen einschließlich TODO-Status und Verknüpfungen in pinned. Beziehungssuchen unterstützen öffentliche IDs für offer, category, user, booking, invoice, admin und promotioncode. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_note - Dokumentation: /docs/api/v2/operations/get-v2-note-af1df5e62e80/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/note' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/note"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/note" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_note","summary":"Notizen auflisten","description":"Liest manuelle und Systemnotizen einschließlich TODO-Status und Verknüpfungen in pinned. Beziehungssuchen unterstützen öffentliche IDs für offer, category, user, booking, invoice, admin und promotioncode. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["note"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"note_012345abcdef_123456":{"id":"note_012345abcdef_123456","type":2,"subject":"Rückfrage zum Kurs","message":"Bitte telefonisch melden.","todo":true,"create_time":"2026-10-22T10:00:00+00:00","pinned":{"offer":["offer_012345abcdef_123456"]}}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Note.php","method":"getNote","line":285},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"note","short":"note","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/note Notiz anlegen Erstellt eine manuelle Notiz (type wird serverseitig auf 2 gesetzt) und verknüpft sie mit mindestens einem vorhandenen, kontoeigenen Objekt in pinned. Nur die dokumentierten Pin-Typen sind erlaubt; Werte sind Listen öffentlicher v2-IDs. Bei todo=true entsteht eine offene Aufgabe. data erlaubt ein einzelnes Objekt oder höchstens 100 Objekte als Batch; die Antwort enthält eine ID bzw. eine ID-Liste. - Operation-ID: post_v2_note - Dokumentation: /docs/api/v2/operations/post-v2-note-383c8be08314/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "subject": "Rückfrage zum Kurs", "message": "Bitte telefonisch melden.", "todo": true, "pinned": { "offer": [ "offer_012345abcdef_123456" ] } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/note' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/note"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "subject": "Rückfrage zum Kurs", "message": "Bitte telefonisch melden.", "todo": true, "pinned": { "offer": [ "offer_012345abcdef_123456" ] } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/note" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"subject\":\"Rückfrage zum Kurs\",\"message\":\"Bitte telefonisch melden.\",\"todo\":true,\"pinned\":{\"offer\":[\"offer_012345abcdef_123456\"]}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_note","summary":"Notiz anlegen","description":"Erstellt eine manuelle Notiz (type wird serverseitig auf 2 gesetzt) und verknüpft sie mit mindestens einem vorhandenen, kontoeigenen Objekt in pinned. Nur die dokumentierten Pin-Typen sind erlaubt; Werte sind Listen öffentlicher v2-IDs. Bei todo=true entsteht eine offene Aufgabe. data erlaubt ein einzelnes Objekt oder höchstens 100 Objekte als Batch; die Antwort enthält eine ID bzw. eine ID-Liste.","tags":["note"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue öffentliche ID oder beim Batch eine Liste; insert zeigt das Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"note_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^note_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^note_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"type":{"type":["string","number","boolean","null","array","object"],"description":"Bei Erstellung immer 2 (manuelle Notiz), unabhängig vom Eingabewert; bei Änderung numerische Umwandlung.","examples":[2]},"subject":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Rückfrage zum Kurs"]},"message":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 1000 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Bitte telefonisch melden."]},"todo":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"pinned":{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"offer":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"category":{"type":"array","items":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"booking":{"type":"array","items":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"admin":{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}}},"additionalProperties":false,"anyOf":[{"required":["user"],"properties":{"user":{"minItems":1}}},{"required":["offer"],"properties":{"offer":{"minItems":1}}},{"required":["category"],"properties":{"category":{"minItems":1}}},{"required":["booking"],"properties":{"booking":{"minItems":1}}},{"required":["invoice"],"properties":{"invoice":{"minItems":1}}},{"required":["admin"],"properties":{"admin":{"minItems":1}}},{"required":["promotioncode"],"properties":{"promotioncode":{"minItems":1}}}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter.\n\nVerknüpft die Notiz mit vorhandenen Objekten desselben Kontos. Die Schlüssel user, offer, category, booking, invoice, admin und promotioncode enthalten jeweils Listen passender öffentlicher v2-IDs; Roh-DB-IDs, fremde Objekte und unbekannte Pin-Typen werden abgelehnt. Beim Anlegen und beim Ersetzen der Verknüpfungen muss mindestens eine gültige Referenz erhalten bleiben. Die Beziehungshooks speichern die Verknüpfungen getrennt vom normalen Feldsetter; ein Update von pinned ersetzt die vorherige Auswahl, während ein fehlendes pinned die Verknüpfungen unverändert lässt."}},"additionalProperties":true,"required":["subject","pinned"]},{"type":"array","items":{"type":"object","properties":{"type":{"type":["string","number","boolean","null","array","object"],"description":"Bei Erstellung immer 2 (manuelle Notiz), unabhängig vom Eingabewert; bei Änderung numerische Umwandlung.","examples":[2]},"subject":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Rückfrage zum Kurs"]},"message":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 1000 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Bitte telefonisch melden."]},"todo":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"pinned":{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"offer":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"category":{"type":"array","items":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"booking":{"type":"array","items":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"admin":{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}}},"additionalProperties":false,"anyOf":[{"required":["user"],"properties":{"user":{"minItems":1}}},{"required":["offer"],"properties":{"offer":{"minItems":1}}},{"required":["category"],"properties":{"category":{"minItems":1}}},{"required":["booking"],"properties":{"booking":{"minItems":1}}},{"required":["invoice"],"properties":{"invoice":{"minItems":1}}},{"required":["admin"],"properties":{"admin":{"minItems":1}}},{"required":["promotioncode"],"properties":{"promotioncode":{"minItems":1}}}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter.\n\nVerknüpft die Notiz mit vorhandenen Objekten desselben Kontos. Die Schlüssel user, offer, category, booking, invoice, admin und promotioncode enthalten jeweils Listen passender öffentlicher v2-IDs; Roh-DB-IDs, fremde Objekte und unbekannte Pin-Typen werden abgelehnt. Beim Anlegen und beim Ersetzen der Verknüpfungen muss mindestens eine gültige Referenz erhalten bleiben. Die Beziehungshooks speichern die Verknüpfungen getrennt vom normalen Feldsetter; ein Update von pinned ersetzt die vorherige Auswahl, während ein fehlendes pinned die Verknüpfungen unverändert lässt."}},"additionalProperties":true,"required":["subject","pinned"]},"minItems":1,"maxItems":100}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Notiz anlegen","value":{"data":{"subject":"Rückfrage zum Kurs","message":"Bitte telefonisch melden.","todo":true,"pinned":{"offer":["offer_012345abcdef_123456"]}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Note.php","method":"postNote","line":210},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"note","short":"note","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/note Notiz ändern Ändert eine manuelle Notiz. Systemnotizen sind gesperrt. pinned ersetzt die bisherigen Verknüpfungen durch mindestens eine gültige, kontoeigene Referenz. todo wird hier nicht geändert; zum Erledigen die Aktion isdone verwenden. id im JSON-Umschlag ist Pflicht. - Operation-ID: put_v2_note - Dokumentation: /docs/api/v2/operations/put-v2-note-9a7ea1007c53/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "subject": "Rückfrage geklärt", "message": "Telefonat abgeschlossen." }, "id": "note_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/note' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/note"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "subject": "Rückfrage geklärt", "message": "Telefonat abgeschlossen." }, "id": "note_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/note" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"subject\":\"Rückfrage geklärt\",\"message\":\"Telefonat abgeschlossen.\"},\"id\":\"note_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_note","summary":"Notiz ändern","description":"Ändert eine manuelle Notiz. Systemnotizen sind gesperrt. pinned ersetzt die bisherigen Verknüpfungen durch mindestens eine gültige, kontoeigene Referenz. todo wird hier nicht geändert; zum Erledigen die Aktion isdone verwenden. id im JSON-Umschlag ist Pflicht.","tags":["note"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, update das tatsächliche Änderungsergebnis; update=false ist keine Bestätigung einer Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"note_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^note_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":["string","number","boolean","null","array","object"],"description":"Bei Erstellung immer 2 (manuelle Notiz), unabhängig vom Eingabewert; bei Änderung numerische Umwandlung.","examples":[2]},"subject":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Rückfrage zum Kurs"]},"message":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 1000 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Bitte telefonisch melden."]},"pinned":{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"offer":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"category":{"type":"array","items":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"booking":{"type":"array","items":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"admin":{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}}},"additionalProperties":false,"anyOf":[{"required":["user"],"properties":{"user":{"minItems":1}}},{"required":["offer"],"properties":{"offer":{"minItems":1}}},{"required":["category"],"properties":{"category":{"minItems":1}}},{"required":["booking"],"properties":{"booking":{"minItems":1}}},{"required":["invoice"],"properties":{"invoice":{"minItems":1}}},{"required":["admin"],"properties":{"admin":{"minItems":1}}},{"required":["promotioncode"],"properties":{"promotioncode":{"minItems":1}}}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter.\n\nVerknüpft die Notiz mit vorhandenen Objekten desselben Kontos. Die Schlüssel user, offer, category, booking, invoice, admin und promotioncode enthalten jeweils Listen passender öffentlicher v2-IDs; Roh-DB-IDs, fremde Objekte und unbekannte Pin-Typen werden abgelehnt. Beim Anlegen und beim Ersetzen der Verknüpfungen muss mindestens eine gültige Referenz erhalten bleiben. Die Beziehungshooks speichern die Verknüpfungen getrennt vom normalen Feldsetter; ein Update von pinned ersetzt die vorherige Auswahl, während ein fehlendes pinned die Verknüpfungen unverändert lässt."}},"additionalProperties":true},"id":{"type":"string","pattern":"^note_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Notiz ändern","value":{"data":{"subject":"Rückfrage geklärt","message":"Telefonat abgeschlossen."},"id":"note_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Note.php","method":"updateNote","line":227},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"note","short":"note","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/note/{id} Notiz lesen Liest manuelle und Systemnotizen einschließlich TODO-Status und Verknüpfungen in pinned. Beziehungssuchen unterstützen öffentliche IDs für offer, category, user, booking, invoice, admin und promotioncode. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_note_id - Dokumentation: /docs/api/v2/operations/get-v2-note-id-7a2d464c0fd6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/note/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/note/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/note/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_note_id","summary":"Notiz lesen","description":"Liest manuelle und Systemnotizen einschließlich TODO-Status und Verknüpfungen in pinned. Beziehungssuchen unterstützen öffentliche IDs für offer, category, user, booking, invoice, admin und promotioncode. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["note"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"note_012345abcdef_123456":{"id":"note_012345abcdef_123456","type":2,"subject":"Rückfrage zum Kurs","message":"Bitte telefonisch melden.","todo":true,"create_time":"2026-10-22T10:00:00+00:00","pinned":{"offer":["offer_012345abcdef_123456"]}}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Note.php","method":"getNote","line":285},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"note","short":"note","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/note/{id} Notiz ändern Ändert eine manuelle Notiz. Systemnotizen sind gesperrt. pinned ersetzt die bisherigen Verknüpfungen durch mindestens eine gültige, kontoeigene Referenz. todo wird hier nicht geändert; zum Erledigen die Aktion isdone verwenden. Die öffentliche ID im Pfad wählt das Objekt; ein zusätzliches id im Umschlag hat beim Legacy-Normalisierer Vorrang. - Operation-ID: put_v2_note_id - Dokumentation: /docs/api/v2/operations/put-v2-note-id-5ef8bb47a4ba/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "subject": "Rückfrage geklärt", "message": "Telefonat abgeschlossen." } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/note/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/note/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "subject": "Rückfrage geklärt", "message": "Telefonat abgeschlossen." } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/note/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"subject\":\"Rückfrage geklärt\",\"message\":\"Telefonat abgeschlossen.\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_note_id","summary":"Notiz ändern","description":"Ändert eine manuelle Notiz. Systemnotizen sind gesperrt. pinned ersetzt die bisherigen Verknüpfungen durch mindestens eine gültige, kontoeigene Referenz. todo wird hier nicht geändert; zum Erledigen die Aktion isdone verwenden. Die öffentliche ID im Pfad wählt das Objekt; ein zusätzliches id im Umschlag hat beim Legacy-Normalisierer Vorrang.","tags":["note"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, update das tatsächliche Änderungsergebnis; update=false ist keine Bestätigung einer Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"note_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^note_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":["string","number","boolean","null","array","object"],"description":"Bei Erstellung immer 2 (manuelle Notiz), unabhängig vom Eingabewert; bei Änderung numerische Umwandlung.","examples":[2]},"subject":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Rückfrage zum Kurs"]},"message":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 1000 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Bitte telefonisch melden."]},"pinned":{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"offer":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"category":{"type":"array","items":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"booking":{"type":"array","items":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"admin":{"type":"array","items":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}}},"additionalProperties":false,"anyOf":[{"required":["user"],"properties":{"user":{"minItems":1}}},{"required":["offer"],"properties":{"offer":{"minItems":1}}},{"required":["category"],"properties":{"category":{"minItems":1}}},{"required":["booking"],"properties":{"booking":{"minItems":1}}},{"required":["invoice"],"properties":{"invoice":{"minItems":1}}},{"required":["admin"],"properties":{"admin":{"minItems":1}}},{"required":["promotioncode"],"properties":{"promotioncode":{"minItems":1}}}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter.\n\nVerknüpft die Notiz mit vorhandenen Objekten desselben Kontos. Die Schlüssel user, offer, category, booking, invoice, admin und promotioncode enthalten jeweils Listen passender öffentlicher v2-IDs; Roh-DB-IDs, fremde Objekte und unbekannte Pin-Typen werden abgelehnt. Beim Anlegen und beim Ersetzen der Verknüpfungen muss mindestens eine gültige Referenz erhalten bleiben. Die Beziehungshooks speichern die Verknüpfungen getrennt vom normalen Feldsetter; ein Update von pinned ersetzt die vorherige Auswahl, während ein fehlendes pinned die Verknüpfungen unverändert lässt."}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Notiz ändern","value":{"data":{"subject":"Rückfrage geklärt","message":"Telefonat abgeschlossen."}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Note.php","method":"updateNote","line":227},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"note","short":"note","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/note/{id}/isdone TODO erledigen und gegebenenfalls Stornierung freigeben Erledigt eine noch offene TODO-Notiz, ergänzt den Nachrichtentext um Zeitpunkt/Administrator und entfernt todo. Gehört die Notiz zu einer ausstehenden Stornierungsanfrage, wird zuerst deren Freigabe ausgeführt; ein Fehler verhindert das Erledigen. Bereits erledigte Notizen liefern not_in_todo_status. Kein JSON-Body erforderlich. - Operation-ID: put_v2_note_id_isdone - Dokumentation: /docs/api/v2/operations/put-v2-note-id-isdone-5a5a8d6def8f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/note/'"${PATH_ID}"'/isdone' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/note/{id}/isdone"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "PUT", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/note/{id}/isdone" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_note_id_isdone","summary":"TODO erledigen und gegebenenfalls Stornierung freigeben","description":"Erledigt eine noch offene TODO-Notiz, ergänzt den Nachrichtentext um Zeitpunkt/Administrator und entfernt todo. Gehört die Notiz zu einer ausstehenden Stornierungsanfrage, wird zuerst deren Freigabe ausgeführt; ein Fehler verhindert das Erledigen. Bereits erledigte Notizen liefern not_in_todo_status. Kein JSON-Body erforderlich.","tags":["note"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Notiz-ID, ergänzter Nachrichtentext und update=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"note_012345abcdef_123456","message":"Bitte telefonisch melden.
Erledigt","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^note_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"message":{"type":"string"},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","message","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Notefunc.php","line":232},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Note.php","method":"updateNote","line":227,"branch":{"file":"application/models/v2/Note.php","line":258}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"note","short":"note","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/note/list Notizen auflisten Liest manuelle und Systemnotizen einschließlich TODO-Status und Verknüpfungen in pinned. Beziehungssuchen unterstützen öffentliche IDs für offer, category, user, booking, invoice, admin und promotioncode. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_note_list - Dokumentation: /docs/api/v2/operations/get-v2-note-list-53b988db8530/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/note/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/note/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/note/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_note_list","summary":"Notizen auflisten","description":"Liest manuelle und Systemnotizen einschließlich TODO-Status und Verknüpfungen in pinned. Beziehungssuchen unterstützen öffentliche IDs für offer, category, user, booking, invoice, admin und promotioncode. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["note"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"note_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"note_012345abcdef_123456":{"id":"note_012345abcdef_123456","type":2,"subject":"Rückfrage zum Kurs","message":"Bitte telefonisch melden.","todo":true,"create_time":"2026-10-22T10:00:00+00:00","pinned":{"offer":["offer_012345abcdef_123456"]}}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},"type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"message":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":64,"type":"string"},"todo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":73,"type":"boolean"},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Note.php","line":83,"lineStart":83,"lineEnd":93,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"847028498f7c3645b2f58a8fc95175137900876cd5d71122703b02ecacbe1838","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Note.php","line":210,"lineStart":210,"lineEnd":216,"sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d","spanSha256":"b24296456b7b463c1b2d9ff3dbdc06f338b67791654193656596a47873f0e771","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"pinned":{"anyOf":[{"type":"object","properties":{"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"offer":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"category":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"invoice":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"admin":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"promotioncode":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true},{"type":"array","maxItems":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Notefunc.php","line":7,"lineEnd":11,"reason":"PUT validates the complete pinned object before even loading existing pin relations.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":105,"lineEnd":109,"reason":"POST before-insert runs the same full secondary-target validation; an empty final pin set is not a supported create.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":139,"lineEnd":142,"reason":"POST relation insertion rechecks the complete normalized pin set.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Notefunc.php","line":216,"lineEnd":259,"reason":"Seven exact resource groups require list values and typed public IDs; batched same-account existence and resource subtype checks precede all pin changes.","sha256":"cb4cf09a6fd8a88f94bca520229db0ab300b37f23d9309e176955a6839e74c0c"},{"file":"application/models/v2/Note.php","line":171,"lineEnd":201,"reason":"Response still groups public IDs by resource; an empty stored result is PHP [].","sha256":"943c7906d13ad4dfa8fdbc3f78de3334f21641a669818eb9f863a151c677854d"}],"description":"Connects a note to account-owned resources using their public v2 IDs.\nPin relation hooks apply changes separately from the storage getter."}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Note.php","method":"getNote","line":285},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"note","short":"note","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer Angebote auflisten Liest Angebote im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert Angebote ohne endgültig gelöschte Einträge; Archiv- oder Papierkorbeinträge sind ohne entsprechende Filter nicht automatisch ausgeschlossen. Über fields können zusätzlich hasDate, shareurl, internal_code und calendar_week angefordert werden; admin ist ein zusätzlicher Filter auf Terminleitungen. - Operation-ID: get_v2_offer - Dokumentation: /docs/api/v2/operations/get-v2-offer-2d86092592a6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer","summary":"Angebote auflisten","description":"Liest Angebote im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert Angebote ohne endgültig gelöschte Einträge; Archiv- oder Papierkorbeinträge sind ohne entsprechende Filter nicht automatisch ausgeschlossen. Über fields können zusätzlich hasDate, shareurl, internal_code und calendar_week angefordert werden; admin ist ein zusätzlicher Filter auf Terminleitungen.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offer_012345abcdef_123456":{"id":"offer_012345abcdef_123456","name":"Beispielangebot","number":"BEISPIEL-2026","status":0,"type":"offer","category":"category_012345abcdef_123456","user_maximum":20,"hasDate":true,"calendar_week":45}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string"},"hasDate":{"type":"boolean"},"shareurl":{"type":"string"},"calendar_week":{"type":"integer"},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string"},"hasDate":{"type":"boolean"},"shareurl":{"type":"string"},"calendar_week":{"type":"integer"},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true}}],"description":"ID-indizierte Sammlung; response_type=array liefert eine Liste. Leere Ergebnismengen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/offer Angebot anlegen Legt Angebot mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. name darf zusätzlich als Liste von {language,value} übergeben werden; die Kontostandardsprache muss enthalten sein, weitere aktive Kontosprachen werden nachgetragen. Baum- und Abo-Regeln werden serverseitig geprüft. type=offer legt ein Angebot unter einer aktiven Kategorie oder einem geeigneten Multi-Angebot an. Archiv und Papierkorb sind als Ziel unzulässig. - Operation-ID: post_v2_offer - Dokumentation: /docs/api/v2/operations/post-v2-offer-1826a986b190/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example offer", "category": "category_012345abcdef_123456", "type": "offer" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/offer' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example offer", "category": "category_012345abcdef_123456", "type": "offer" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example offer\",\"category\":\"category_012345abcdef_123456\",\"type\":\"offer\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_offer","summary":"Angebot anlegen","description":"Legt Angebot mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. name darf zusätzlich als Liste von {language,value} übergeben werden; die Kontostandardsprache muss enthalten sein, weitere aktive Kontosprachen werden nachgetragen. Baum- und Abo-Regeln werden serverseitig geprüft. type=offer legt ein Angebot unter einer aktiven Kategorie oder einem geeigneten Multi-Angebot an. Archiv und Papierkorb sind als Ziel unzulässig.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"name":{"anyOf":[{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":31},{"type":"array","items":{"type":"object","required":["language","value"],"properties":{"language":{"type":"string"},"value":{"type":"string","maxLength":80}},"additionalProperties":true}}],"description":"Offer::postOffer also accepts localized name entries, selecting the account default language."},"number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":59},"row_lft":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":68,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_rgt":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":78,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"user_maximum":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":88,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":104,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"user_minimal":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":122,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":137},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":147},"date_close":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":157},"date_displayend_reference":{"x-edoobox-knownInputs":["date_start","date_end","date_close"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["date_start"],"x-edoobox-sourceLine":176,"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":203,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_displaystart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":218},"date_signupstart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":227},"image":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"place":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":256},"category":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":269},"offerdef":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":293},"type":{"x-edoobox-knownInputs":["offer","folder","archive","trash"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":308},"mode":{"enum":["Online","Onsite","Blended"],"x-edoobox-sourceLine":347},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":365,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"archive":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":380,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"trash":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":392,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"waiting_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":404,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mustpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":413,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"countdown":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":422,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"lesson_booking":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":437,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":449,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration_type":{"x-edoobox-knownInputs":["named_users","participant_count","named_users_contact"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["named_users"],"x-edoobox-sourceLine":459},"barcode_ticket":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":486,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":495},"multi_offer":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":508,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multi_offer_child":{"readOnly":true,"description":"Getter returns null without reading input. Before-insert derives child-parent relation from validated category/parent (kat_id) and overwrites it for multi-offer children. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":566},"shortdescription":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces normal/include/exclude tag associations. Every supplied tag must be a typed, existing same-account tag and every mode must be recognized before relation writes. Duplicate associations are normalized.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":40,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":640}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","category","type"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"name":{"anyOf":[{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":31},{"type":"array","items":{"type":"object","required":["language","value"],"properties":{"language":{"type":"string"},"value":{"type":"string","maxLength":80}},"additionalProperties":true}}],"description":"Offer::postOffer also accepts localized name entries, selecting the account default language."},"number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":59},"row_lft":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":68,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_rgt":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":78,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"user_maximum":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":88,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":104,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"user_minimal":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":122,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":137},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":147},"date_close":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":157},"date_displayend_reference":{"x-edoobox-knownInputs":["date_start","date_end","date_close"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["date_start"],"x-edoobox-sourceLine":176,"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":203,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_displaystart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":218},"date_signupstart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":227},"image":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"place":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":256},"category":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":269},"offerdef":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":293},"type":{"x-edoobox-knownInputs":["offer","folder","archive","trash"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":308},"mode":{"enum":["Online","Onsite","Blended"],"x-edoobox-sourceLine":347},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":365,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"archive":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":380,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"trash":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":392,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"waiting_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":404,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mustpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":413,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"countdown":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":422,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"lesson_booking":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":437,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":449,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration_type":{"x-edoobox-knownInputs":["named_users","participant_count","named_users_contact"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["named_users"],"x-edoobox-sourceLine":459},"barcode_ticket":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":486,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":495},"multi_offer":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":508,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multi_offer_child":{"readOnly":true,"description":"Getter returns null without reading input. Before-insert derives child-parent relation from validated category/parent (kat_id) and overwrites it for multi-offer children. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":566},"shortdescription":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces normal/include/exclude tag associations. Every supplied tag must be a typed, existing same-account tag and every mode must be recognized before relation writes. Duplicate associations are normalized.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":40,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":640}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","category","type"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"name":"Example offer","category":"category_012345abcdef_123456","type":"offer"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"postOffer","line":681},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer Angebot aktualisieren Ändert die übergebenen schreibbaren Felder in data für Angebot. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. - Operation-ID: put_v2_offer - Dokumentation: /docs/api/v2/operations/put-v2-offer-cb4e46b184eb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "number": "EXAMPLE-2026" }, "id": "offer_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "number": "EXAMPLE-2026" }, "id": "offer_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"number\":\"EXAMPLE-2026\"},\"id\":\"offer_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer","summary":"Angebot aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Angebot. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":false,"number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":59},"row_lft":false,"row_rgt":false,"user_maximum":{"x-edoobox-canonicalType":"integer","description":"PHP int cast then clamp to 0..99999. Original out-of-range values are normalized, not rejected.\n\nNormalize/clamp, then verify against runtime subscription maximum.\n\nRuntime preconditions / rejection: Normalized capacity above active subscription maximum emits ED40240.","x-edoobox-schemaStatus":"source-reviewed","examples":[25],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":false,"user_minimal":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":122,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start":false,"date_end":false,"date_close":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":157},"date_displayend_reference":{"x-edoobox-knownInputs":["date_start","date_end","date_close"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["date_start"],"x-edoobox-sourceLine":176,"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":203,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_displaystart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":218},"date_signupstart":{"x-edoobox-canonicalType":"string","description":"ISO date-time text is canonical. PHP strtotime accepts broader text; invalid/empty strings normalize to timestamp 0 rather than reject.\n\ndP returns parsed timestamp or 0. Positive timestamp requires offer_signup_start subscription capability.\n\nRuntime preconditions / rejection: Positive timestamp with unavailable feature emits ED40240.","x-edoobox-schemaStatus":"source-reviewed","examples":["2030-06-01T09:00:00+02:00"],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":false,"place":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":256},"category":false,"offerdef":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":293},"type":false,"mode":{"enum":["Online","Onsite","Blended"],"x-edoobox-sourceLine":347},"status":{"x-edoobox-canonicalType":"integer","description":"Cast result must be 0..5. Numeric strings and other PHP int-coercible values can also pass. This is not a strict integer wire validator.\n\nCallback casts itself and rejects out-of-range cast results; unlike getter it does not remap out-of-range to 0.\n\nRuntime preconditions / rejection: Cast result outside 0..5 -> callback false -> ED90100. Missing offer or absent positive capacity/start/close dates -> ED90100. Statuses 1/2 require future registration close date; otherwise date_close_must_be_in_the_future.","x-edoobox-recognizedValues":[0,1,2,3,4,5],"x-edoobox-schemaStatus":"source-reviewed","examples":[1],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":false,"trash":false,"waiting_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":404,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mustpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":413,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"countdown":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":422,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"lesson_booking":{"x-edoobox-canonicalType":"boolean","description":"Canonical JSON booleans supported; PHP loose truth comparison also coerces other JSON values. No strict wire boolean validator.\n\nHook bypasses getter but uses equivalent ==true conversion to y/n.\n\nRuntime preconditions / rejection: Existing bookings cause callback false -> ED90100, including redundant assignments. Enable requires lesson_booking subscription capability (ED40240). Enable with existing offerlist throws offerlist_not_allowed.","x-edoobox-schemaStatus":"source-reviewed","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":449,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration_type":{"x-edoobox-knownInputs":["named_users","participant_count","named_users_contact"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["named_users"],"x-edoobox-sourceLine":459},"barcode_ticket":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":486,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":495},"multi_offer":false,"multi_offer_child":false,"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":566},"shortdescription":false,"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces normal/include/exclude tag associations. Every supplied tag must be a typed, existing same-account tag and every mode must be recognized before relation writes. Duplicate associations are normalized.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"x-edoobox-canonicalType":"string","description":"aP(...,true,40) trims, flattens line breaks, escapes and truncates strings; nonstrings become empty. Longer input is not rejected.\n\nString normalized to at most 40 characters after escaping. Category propagates to descendants; both disable firmaConnect.\n\nRuntime preconditions / rejection: Nonempty code requires subscription internal_function; absence emits ED40240. Missing/wrong-kind resource: category callback false -> ED90100; offer throws offer_not_found.","x-edoobox-schemaStatus":"source-reviewed","examples":["INTERNAL-SAMPLE",""],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["name","row_lft","row_rgt","order","date_start","date_end","image","category","type","archive","trash","multi_offer","multi_offer_child","shortdescription"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Angebot aktualisieren","value":{"data":{"number":"EXAMPLE-2026"},"id":"offer_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/offer/{id} Angebot löschen Löscht Angebot im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Löscht den vollständigen erreichbaren Teilbaum logisch und entfernt ihn aus dem aktiven Baum. Statusregeln können die Löschung blockieren; data.delete=false und error_code beachten. Ein einzelnes Kind eines Multi-Angebots darf hier nicht gelöscht werden. - Operation-ID: delete_v2_offer_id - Dokumentation: /docs/api/v2/operations/delete-v2-offer-id-50c8817f7ba7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "offer_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "offer_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"offer_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_offer_id","summary":"Angebot löschen","description":"Löscht Angebot im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Löscht den vollständigen erreichbaren Teilbaum logisch und entfernt ihn aus dem aktiven Baum. Statusregeln können die Löschung blockieren; data.delete=false und error_code beachten. Ein einzelnes Kind eines Multi-Angebots darf hier nicht gelöscht werden.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"delete":{"type":"boolean"},"error_code":{"type":"string"},"block_offers":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Angebot löschen","value":{"id":"offer_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"deleteOffer","line":855},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/{id} Angebot abrufen Liest Angebot anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Liefert Angebote ohne endgültig gelöschte Einträge; Archiv- oder Papierkorbeinträge sind ohne entsprechende Filter nicht automatisch ausgeschlossen. Über fields können zusätzlich hasDate, shareurl, internal_code und calendar_week angefordert werden; admin ist ein zusätzlicher Filter auf Terminleitungen. - Operation-ID: get_v2_offer_id - Dokumentation: /docs/api/v2/operations/get-v2-offer-id-273a09c077e8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_id","summary":"Angebot abrufen","description":"Liest Angebot anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Liefert Angebote ohne endgültig gelöschte Einträge; Archiv- oder Papierkorbeinträge sind ohne entsprechende Filter nicht automatisch ausgeschlossen. Über fields können zusätzlich hasDate, shareurl, internal_code und calendar_week angefordert werden; admin ist ein zusätzlicher Filter auf Terminleitungen.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offer_012345abcdef_123456":{"id":"offer_012345abcdef_123456","name":"Beispielangebot","number":"BEISPIEL-2026","status":0,"type":"offer","category":"category_012345abcdef_123456","user_maximum":20,"hasDate":true,"calendar_week":45}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string"},"hasDate":{"type":"boolean"},"shareurl":{"type":"string"},"calendar_week":{"type":"integer"},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string"},"hasDate":{"type":"boolean"},"shareurl":{"type":"string"},"calendar_week":{"type":"integer"},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true}}],"description":"ID-indizierte Sammlung; response_type=array liefert eine Liste. Leere Ergebnismengen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id} Angebot aktualisieren Ändert die übergebenen schreibbaren Felder in data für Angebot. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. - Operation-ID: put_v2_offer_id - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-13b3f4bc2491/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "number": "EXAMPLE-2026" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "number": "EXAMPLE-2026" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"number\":\"EXAMPLE-2026\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id","summary":"Angebot aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Angebot. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":false,"number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":59},"row_lft":false,"row_rgt":false,"user_maximum":{"x-edoobox-canonicalType":"integer","description":"PHP int cast then clamp to 0..99999. Original out-of-range values are normalized, not rejected.\n\nNormalize/clamp, then verify against runtime subscription maximum.\n\nRuntime preconditions / rejection: Normalized capacity above active subscription maximum emits ED40240.","x-edoobox-schemaStatus":"source-reviewed","examples":[25],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"order":false,"user_minimal":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":122,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start":false,"date_end":false,"date_close":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":157},"date_displayend_reference":{"x-edoobox-knownInputs":["date_start","date_end","date_close"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["date_start"],"x-edoobox-sourceLine":176,"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":203,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_displaystart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":218},"date_signupstart":{"x-edoobox-canonicalType":"string","description":"ISO date-time text is canonical. PHP strtotime accepts broader text; invalid/empty strings normalize to timestamp 0 rather than reject.\n\ndP returns parsed timestamp or 0. Positive timestamp requires offer_signup_start subscription capability.\n\nRuntime preconditions / rejection: Positive timestamp with unavailable feature emits ED40240.","x-edoobox-schemaStatus":"source-reviewed","examples":["2030-06-01T09:00:00+02:00"],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"image":false,"place":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":256},"category":false,"offerdef":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":293},"type":false,"mode":{"enum":["Online","Onsite","Blended"],"x-edoobox-sourceLine":347},"status":{"x-edoobox-canonicalType":"integer","description":"Cast result must be 0..5. Numeric strings and other PHP int-coercible values can also pass. This is not a strict integer wire validator.\n\nCallback casts itself and rejects out-of-range cast results; unlike getter it does not remap out-of-range to 0.\n\nRuntime preconditions / rejection: Cast result outside 0..5 -> callback false -> ED90100. Missing offer or absent positive capacity/start/close dates -> ED90100. Statuses 1/2 require future registration close date; otherwise date_close_must_be_in_the_future.","x-edoobox-recognizedValues":[0,1,2,3,4,5],"x-edoobox-schemaStatus":"source-reviewed","examples":[1],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"archive":false,"trash":false,"waiting_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":404,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mustpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":413,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"countdown":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":422,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"lesson_booking":{"x-edoobox-canonicalType":"boolean","description":"Canonical JSON booleans supported; PHP loose truth comparison also coerces other JSON values. No strict wire boolean validator.\n\nHook bypasses getter but uses equivalent ==true conversion to y/n.\n\nRuntime preconditions / rejection: Existing bookings cause callback false -> ED90100, including redundant assignments. Enable requires lesson_booking subscription capability (ED40240). Enable with existing offerlist throws offerlist_not_allowed.","x-edoobox-schemaStatus":"source-reviewed","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"multiple_registration":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":449,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration_type":{"x-edoobox-knownInputs":["named_users","participant_count","named_users_contact"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["named_users"],"x-edoobox-sourceLine":459},"barcode_ticket":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":486,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":495},"multi_offer":false,"multi_offer_child":false,"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":566},"shortdescription":false,"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces normal/include/exclude tag associations. Every supplied tag must be a typed, existing same-account tag and every mode must be recognized before relation writes. Duplicate associations are normalized.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"x-edoobox-canonicalType":"string","description":"aP(...,true,40) trims, flattens line breaks, escapes and truncates strings; nonstrings become empty. Longer input is not rejected.\n\nString normalized to at most 40 characters after escaping. Category propagates to descendants; both disable firmaConnect.\n\nRuntime preconditions / rejection: Nonempty code requires subscription internal_function; absence emits ED40240. Missing/wrong-kind resource: category callback false -> ED90100; offer throws offer_not_found.","x-edoobox-schemaStatus":"source-reviewed","examples":["INTERNAL-SAMPLE",""],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["name","row_lft","row_rgt","order","date_start","date_end","image","category","type","archive","trash","multi_offer","multi_offer_child","shortdescription"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"number":"EXAMPLE-2026"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/{id}/attendance_list.html Präsenzliste als HTML abrufen Rendert die Präsenzliste mit Terminen als direktes HTML-Dokument. Die Pfad-ID wählt das Angebot. Die Ausgabe kann personenbezogene Teilnehmerdaten enthalten; sie erzeugt keine Buchung. Der language-Header wählt die Ausgabesprache. - Operation-ID: get_v2_offer_id_attendance_list_html - Dokumentation: /docs/api/v2/operations/get-v2-offer-id-attendance-list-html-f24884f4eac5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/attendance_list.html' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/attendance_list.html"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/attendance_list.html" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_id_attendance_list_html","summary":"Präsenzliste als HTML abrufen","description":"Rendert die Präsenzliste mit Terminen als direktes HTML-Dokument. Die Pfad-ID wählt das Angebot. Die Ausgabe kann personenbezogene Teilnehmerdaten enthalten; sie erzeugt keine Buchung. Der language-Header wählt die Ausgabesprache.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Gerendertes HTML-Dokument mit Teilnehmerdaten.","content":{"text/html":{"schema":{"type":"string"},"example":"

Beispielangebot

Teilnehmer
Beispielperson
"}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1340}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id}/auto_archive Angebot automatisch archivieren Archiviert das Angebot und seinen zulässigen Teilbaum im anhand des Angebotsdatums bestimmten Archivpfad. Benötigt nur die Ziel-ID; data wird nicht gelesen. Angebotsstatus, bestehender Archivstatus und Multi-Angebotsbeziehungen können den Vorgang blockieren; update und error_code prüfen. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_id_auto_archive - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-auto-archive-5226630dc3f2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/auto_archive' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/auto_archive"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/auto_archive" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id_auto_archive","summary":"Angebot automatisch archivieren","description":"Archiviert das Angebot und seinen zulässigen Teilbaum im anhand des Angebotsdatums bestimmten Archivpfad. Benötigt nur die Ziel-ID; data wird nicht gelesen. Angebotsstatus, bestehender Archivstatus und Multi-Angebotsbeziehungen können den Vorgang blockieren; update und error_code prüfen. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"},"error_code":{"type":"string"},"block_offers":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Offerfunc.php","line":157},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Angebot automatisch archivieren","value":{}}}}},"required":false},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":815}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id}/auto_delete Angebot in den Papierkorb verschieben Verschiebt Angebot samt zulässigem Teilbaum in den Papierkorb. Die Ziel-ID genügt; data wird nicht gelesen. Bereits verschobene Einträge, fehlende Papierkorbwurzel und nicht erlaubte Angebotsstatus werden als update=false mit error_code gemeldet. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_id_auto_delete - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-auto-delete-555b83a2bf1b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/auto_delete' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/auto_delete"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/auto_delete" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id_auto_delete","summary":"Angebot in den Papierkorb verschieben","description":"Verschiebt Angebot samt zulässigem Teilbaum in den Papierkorb. Die Ziel-ID genügt; data wird nicht gelesen. Bereits verschobene Einträge, fehlende Papierkorbwurzel und nicht erlaubte Angebotsstatus werden als update=false mit error_code gemeldet. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"},"error_code":{"type":"string"},"block_offers":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Angebot in den Papierkorb verschieben","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":819}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/offer/{id}/copy Angebot mit optionalen Terminen kopieren Kopiert das Angebot in die Zielkategorie oder ein zulässiges Multi-Angebot. Die Pfad-ID bestimmt die Quelle. category und date_start sind erforderlich; copy_dates aktiviert die verschobene Terminübernahme. copy_membership_conditions ist standardmäßig aktiv und wird durch false, 0, "0" oder "false" ausgeschaltet. number und names (Liste aus {language,value}) können Angaben der Kopie ersetzen. Konto-, Baum- und Multi-Angebotsregeln bleiben maßgeblich. - Operation-ID: post_v2_offer_id_copy - Dokumentation: /docs/api/v2/operations/post-v2-offer-id-copy-7e093e19f3a2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "category": "category_012345abcdef_123456", "date_start": "2026-11-03T09:00:00+01:00", "copy_dates": true, "copy_membership_conditions": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/copy' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/copy"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "category": "category_012345abcdef_123456", "date_start": "2026-11-03T09:00:00+01:00", "copy_dates": true, "copy_membership_conditions": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/copy" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"category\":\"category_012345abcdef_123456\",\"date_start\":\"2026-11-03T09:00:00+01:00\",\"copy_dates\":true,\"copy_membership_conditions\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_offer_id_copy","summary":"Angebot mit optionalen Terminen kopieren","description":"Kopiert das Angebot in die Zielkategorie oder ein zulässiges Multi-Angebot. Die Pfad-ID bestimmt die Quelle. category und date_start sind erforderlich; copy_dates aktiviert die verschobene Terminübernahme. copy_membership_conditions ist standardmäßig aktiv und wird durch false, 0, \"0\" oder \"false\" ausgeschaltet. number und names (Liste aus {language,value}) können Angaben der Kopie ersetzen. Konto-, Baum- und Multi-Angebotsregeln bleiben maßgeblich.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"category":{"anyOf":[{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}]},"date_start":{"type":"string","description":"Von PHP strtotime parsebarer Startwert; ISO 8601 empfohlen."},"copy_dates":{"description":"Wird durch edisset/PHP-Wahrheitsprüfung ausgewertet; kanonisch boolean.","x-edoobox-canonicalType":"boolean"},"copy_membership_conditions":{"description":"Standard true; false, 0, \"0\" und \"false\" deaktivieren die Übernahme.","x-edoobox-canonicalType":"boolean"},"number":{"type":"string"},"names":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true,"required":["language","value"]}}},"additionalProperties":true,"required":["category","date_start"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Angebot mit optionalen Terminen kopieren","value":{"data":{"category":"category_012345abcdef_123456","date_start":"2026-11-03T09:00:00+01:00","copy_dates":true,"copy_membership_conditions":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"postOffer","line":681,"branch":{"file":"application/models/v2/Offer.php","line":687}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/{id}/dashboard Angebotsübersicht mit Buchungen und Auslastung abrufen Liefert das Angebot samt Preiskategorien, den fünf zeitlich nächsten Terminen, Belegungs-/Buchungsstatistik, zugeordneten Frage-/Leistungsgruppen, Anfragelisten, Promotionen und Tags. Dazu kommen bis zu 20 aktuelle Buchungen und deren Teilnehmerdaten. Bei Multi-Angeboten werden Eltern-/Kinddaten ergänzt. Gültige Reservierungstimer reduzieren freie Plätze; negative Angebotskapazität kann eine Überbuchung anzeigen. Ein gelöschtes oder im Papierkorb befindliches Angebot wird zurückgewiesen. fields und Listenpaginierung ändern den Dashboardumfang nicht. - Operation-ID: get_v2_offer_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-offer-id-dashboard-fc09b5f59e04/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_id_dashboard","summary":"Angebotsübersicht mit Buchungen und Auslastung abrufen","description":"Liefert das Angebot samt Preiskategorien, den fünf zeitlich nächsten Terminen, Belegungs-/Buchungsstatistik, zugeordneten Frage-/Leistungsgruppen, Anfragelisten, Promotionen und Tags. Dazu kommen bis zu 20 aktuelle Buchungen und deren Teilnehmerdaten. Bei Multi-Angeboten werden Eltern-/Kinddaten ergänzt. Gültige Reservierungstimer reduzieren freie Plätze; negative Angebotskapazität kann eine Überbuchung anzeigen. Ein gelöschtes oder im Papierkorb befindliches Angebot wird zurückgewiesen. fields und Listenpaginierung ändern den Dashboardumfang nicht.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offer_id":"offer_012345abcdef_123456","offer":{"id":"offer_012345abcdef_123456","name":"Beispielangebot","user_maximum":20},"pricecategorys":false,"performing_pricecategory":[],"closest_dates_5":[],"count_dates":0,"booking_stats":{"total_revenue":0,"total_bookings_default":0,"total_bookings_waitinglist":0,"total_bookings_canceled":0,"total_bookings_mustpay":0},"bookings":[],"used_promocodes":[],"user_stats":{"count_users":0,"free_places":20,"maximum_places":20,"reserved_places":0,"count_users_waitinglist":0},"users":[],"promotions":false,"performing_promotion":[],"tags":[],"questiongroups":false,"deliverablesgroups":false,"offerlist":false,"offer_parent":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"offer_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"offer":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true},"offer_parent":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"const":false}]},"multi_offer_childs":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"pricecategorys":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"performing_pricecategory":{"type":"array","items":{"type":"object","properties":{"pricecategory_id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["pricecategory_012345abcdef_123456"]},"assigned":{"type":"integer"},"reserved":{"type":"integer"},"available":{"type":"integer"}},"additionalProperties":true,"required":["pricecategory_id","assigned","reserved","available"]}},"closest_dates_5":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["date_012345abcdef_123456"]},"description":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"string"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string"},{"const":false}]},"place":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":77,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":84,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"room":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":90,"reason":"Falsey write input becomes zero; otherwise the public relation ID is decoded.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"},{"file":"application/models/v2/Date.php","line":97,"reason":"Response is a public relation ID or false.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":101,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"leader":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Datefunc.php","line":174,"lineEnd":179,"reason":"PUT obtains a fully validated leader list before modifying assignments.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":239,"lineEnd":243,"reason":"POST relation callback consumes the same validated list.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Datefunc.php","line":266,"lineEnd":289,"reason":"Strict list, typed admin decoding, positive IDs and deduplicated same-account user_info existence checks precede relation mutations.","sha256":"66cb9ba28b2fba16b68a1f1d213c72010ba38750e8bdb34deb9d82bbdf630b1a"},{"file":"application/models/v2/Date.php","line":131,"reason":"Response maps joined leaders to an array of public administrator IDs.","sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264"}],"description":"Assigns administrators as leaders of this date within the current account.\nThe relation hook applies assignments separately from the storage getter."},"color":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Date.php","line":153,"lineStart":153,"lineEnd":172,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"2ec5847a45af791e05db717f2e4f5946198cf290fc3c441b95d55d54f6045814","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"aP text normalization and aQ output. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Date.php","line":189,"lineStart":189,"lineEnd":202,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"d6306bef7b4617f803aff2ac2de39df94f0233667a11ff2c4b269b9a46d47987","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Date.php","line":237,"lineStart":237,"lineEnd":244,"sha256":"6c2cc9b158ba60e211d7da77a920adc45001382fdb859ff62c711562d493b264","spanSha256":"ccfc31e1b13732e53c6b5ff2f95400c7664e404c4bc10eb0645152b9e3f4bb47","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true}},"count_dates":{"type":"integer"},"booking_stats":{"type":"object","properties":{"total_revenue":{"type":"number"},"total_bookings_default":{"type":"integer"},"total_bookings_waitinglist":{"type":"integer"},"total_bookings_canceled":{"type":"integer"},"total_bookings_mustpay":{"type":"integer"}},"additionalProperties":true},"bookings":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},"used_promocodes":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},"user_stats":{"type":"object","properties":{"count_users":{"type":"integer"},"free_places":{"type":"integer"},"maximum_places":{"type":"integer"},"reserved_places":{"type":"integer"},"count_users_waitinglist":{"type":"integer"}},"additionalProperties":true},"users":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"promotions":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"performing_promotion":{"type":"array","items":{"type":"object","properties":{"promotion_id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."},"assigned":{"type":"integer"}},"additionalProperties":true,"required":["promotion_id","assigned"]}},"tags":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"questiongroups":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"deliverablesgroups":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]},"offerlist":{"anyOf":[{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},{"const":false}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/dashboard/Offerdashboard.php","line":12},"reason":"Complete reachable application graph reviewed: own SELECTs, fresh Eddata generic reads, balance allocation SELECTs, source-only availability formatting. Does not reach offer tree repair, booking certificates, queue cleanup or sending/render actions. Derived read caches are not domain changes.","relatedSources":[{"file":"application/models/v2/Booking.php","line":1561},{"file":"application/models/v2/Positivebalance.php","line":440}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1348}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id}/deliverablesgroup Zusatzleistungsgruppe einem Angebot zuordnen Fügt die Zusatzleistungsgruppe zum Angebot hinzu oder entfernt die Zuordnung. change=update ändert die Sortierung; sort wird numerisch normalisiert und ist optional (Standard 0). Bei Kindern von Multi-Angeboten sind neue oder geänderte Zusatzleistungszuordnungen gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_id_deliverablesgroup - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-deliverablesgroup-1a8787de0a96/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "deliverablesgroup": "deliverablesgroup_012345abcdef_123456", "change": "add", "sort": 0 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/deliverablesgroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/deliverablesgroup"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "deliverablesgroup": "deliverablesgroup_012345abcdef_123456", "change": "add", "sort": 0 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/deliverablesgroup" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"deliverablesgroup\":\"deliverablesgroup_012345abcdef_123456\",\"change\":\"add\",\"sort\":0}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id_deliverablesgroup","summary":"Zusatzleistungsgruppe einem Angebot zuordnen","description":"Fügt die Zusatzleistungsgruppe zum Angebot hinzu oder entfernt die Zuordnung. change=update ändert die Sortierung; sort wird numerisch normalisiert und ist optional (Standard 0). Bei Kindern von Multi-Angeboten sind neue oder geänderte Zusatzleistungszuordnungen gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deliverablesgroup":{"type":"string","pattern":"^deliverablesgroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["deliverablesgroup_012345abcdef_123456"]},"change":{"type":"string","enum":["add","remove","update"]},"sort":{"x-edoobox-canonicalType":"integer","description":"PHP-Integernormalisierung; Standard 0, keine strikte JSON-Typprüfung."}},"additionalProperties":true,"required":["deliverablesgroup","change"]},"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zusatzleistungsgruppe einem Angebot zuordnen","value":{"data":{"deliverablesgroup":"deliverablesgroup_012345abcdef_123456","change":"add","sort":0}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":827}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/offer/{id}/files Ressourcendatei löschen Markiert die über ihre files-ID gewählte Datei als gelöscht (iamdelete). Die Pfad-ID ist keine Angebots-ID. Ein JSON-id kann die Ziel-ID überschreiben. Kundenspezifische Exportdateien sind über diese generische Route gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: delete_v2_offer_id_files - Dokumentation: /docs/api/v2/operations/delete-v2-offer-id-files-a3f80930e5d7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche files-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "files_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "files_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"files_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_offer_id_files","summary":"Ressourcendatei löschen","description":"Markiert die über ihre files-ID gewählte Datei als gelöscht (iamdelete). Die Pfad-ID ist keine Angebots-ID. Ein JSON-id kann die Ziel-ID überschreiben. Kundenspezifische Exportdateien sind über diese generische Route gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Ressourcendatei löschen","value":{"id":"files_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"deleteOffer","line":855,"branch":{"file":"application/models/v2/Offer.php","line":861}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/{id}/files Metadaten einer Angebotsdatei abrufen Liest die Metadaten von Dateien mit resource_type=offer. Gelöschte Dateien sowie kundenspezifische Exportdateien werden ausgeschlossen. Die Pfad-ID ist eine files-ID, keine Angebots-ID. - Operation-ID: get_v2_offer_id_files - Dokumentation: /docs/api/v2/operations/get-v2-offer-id-files-229c48367140/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche files-ID der Angebotsdatei. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID der Angebotsdatei.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_id_files","summary":"Metadaten einer Angebotsdatei abrufen","description":"Liest die Metadaten von Dateien mit resource_type=offer. Gelöschte Dateien sowie kundenspezifische Exportdateien werden ausgeschlossen. Die Pfad-ID ist eine files-ID, keine Angebots-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID der Angebotsdatei.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"files_012345abcdef_123456":{"id":"files_012345abcdef_123456","filename":"beispiel.pdf","description":{"de":"Kursunterlagen"},"filetype":"externalOfferFile","resource_type":"offer","resource_id":"offer_012345abcdef_123456","last_change":"2026-10-22T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","items":{"type":"string"}}]},"filetype":{"type":"string"},"resource_type":{"type":"string","const":"offer"},"resource_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"last_change":{"type":"string"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","items":{"type":"string"}}]},"filetype":{"type":"string"},"resource_type":{"type":"string","const":"offer"},"resource_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"last_change":{"type":"string"}},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1354}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id}/files Dateimetadaten aktualisieren Ändert filename und die sprachcodierte description einer vorhandenen Datei. id ist eine files-ID, keine Angebots-ID. Dateibytes, resource_type, resource_id und filetype werden dadurch nicht geändert; kundenspezifische Exportdateien sind über diese Route gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_id_files - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-files-38ceae998231/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche files-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": { "de": "Aktualisierte Kursunterlagen" } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": { "de": "Aktualisierte Kursunterlagen" } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":{\"de\":\"Aktualisierte Kursunterlagen\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id_files","summary":"Dateimetadaten aktualisieren","description":"Ändert filename und die sprachcodierte description einer vorhandenen Datei. id ist eine files-ID, keine Angebots-ID. Dateibytes, resource_type, resource_id und filetype werden dadurch nicht geändert; kundenspezifische Exportdateien sind über diese Route gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"filename":{"type":"string"},"description":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dateimetadaten aktualisieren","value":{"data":{"description":{"de":"Aktualisierte Kursunterlagen"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":836}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id}/image Angebot mit einem Bild versehen Wählt eine kontoeigene Publicfile-Datei als Bild aus und erstellt bzw. verwendet die erforderlichen Bildvarianten. Erlaubt sind JPG, JPEG und PNG; publicfile=false entfernt das Bild. Der Endpunkt verarbeitet keine direkten Datei-Bytes. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_id_image - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-image-2ba69a5dd9a6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "publicfile": "publicfile_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/image' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/image"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "publicfile": "publicfile_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/image" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"publicfile\":\"publicfile_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id_image","summary":"Angebot mit einem Bild versehen","description":"Wählt eine kontoeigene Publicfile-Datei als Bild aus und erstellt bzw. verwendet die erforderlichen Bildvarianten. Erlaubt sind JPG, JPEG und PNG; publicfile=false entfernt das Bild. Der Endpunkt verarbeitet keine direkten Datei-Bytes. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true,"image":"beispiel.jpg","files":[],"url":"https://cdn.example.invalid/beispiel.jpg"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"},"image":{"anyOf":[{"type":"string"},{"const":false}]},"files":{"type":"array","items":{"type":"string"}},"url":{"type":"string"}},"additionalProperties":true,"required":["id","update","image"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"publicfile":{"anyOf":[{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},{"const":false}]}},"additionalProperties":true,"required":["publicfile"]},"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Angebot mit einem Bild versehen","value":{"data":{"publicfile":"publicfile_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":823}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id}/move Angebot im Baum verschieben Verschiebt den vollständigen Knoten unter node (inside) bzw. vor/nach node (before/after). node ist eine öffentliche Kategorie- oder Angebots-ID. Konto-, Zyklus-, Status-, Archiv-, Papierkorb- und Multi-Angebotsregeln werden unter Baumsperre geprüft; ein abgelehntes Ergebnis kann error=false mit update=false enthalten. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_id_move - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-move-67d4211a9017/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "node": "category_012345abcdef_123456", "position": "inside" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/move' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/move"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "node": "category_012345abcdef_123456", "position": "inside" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/move" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"node\":\"category_012345abcdef_123456\",\"position\":\"inside\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id_move","summary":"Angebot im Baum verschieben","description":"Verschiebt den vollständigen Knoten unter node (inside) bzw. vor/nach node (before/after). node ist eine öffentliche Kategorie- oder Angebots-ID. Konto-, Zyklus-, Status-, Archiv-, Papierkorb- und Multi-Angebotsregeln werden unter Baumsperre geprüft; ein abgelehntes Ergebnis kann error=false mit update=false enthalten. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"},"error_code":{"type":"string"},"block_offers":{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"node":{"anyOf":[{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}],"description":"Öffentliche Kategorie- oder Angebots-ID des Zielknotens; der gewählte position-Wert bestimmt die Beziehung."},"position":{"type":"string","enum":["before","after","inside"]}},"additionalProperties":true,"required":["node","position"]},"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Angebot im Baum verschieben","value":{"data":{"node":"category_012345abcdef_123456","position":"inside"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":811}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id}/offerlist Anfrageliste einem Angebot zuordnen Fügt die Anfrageliste zum Angebot hinzu oder entfernt die Zuordnung. Die Kombination mit Multi-Angeboten oder freier Terminauswahl ist nicht zulässig. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_id_offerlist - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-offerlist-7766d25cf1df/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offerlist": "offerlist_012345abcdef_123456", "change": "add" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/offerlist' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/offerlist"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offerlist": "offerlist_012345abcdef_123456", "change": "add" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/offerlist" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offerlist\":\"offerlist_012345abcdef_123456\",\"change\":\"add\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id_offerlist","summary":"Anfrageliste einem Angebot zuordnen","description":"Fügt die Anfrageliste zum Angebot hinzu oder entfernt die Zuordnung. Die Kombination mit Multi-Angeboten oder freier Terminauswahl ist nicht zulässig. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offerlist":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},"change":{"type":"string","enum":["add","remove"]}},"additionalProperties":true,"required":["offerlist","change"]},"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Anfrageliste einem Angebot zuordnen","value":{"data":{"offerlist":"offerlist_012345abcdef_123456","change":"add"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":832}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/{id}/participant_information.html Teilnehmerinformationen als HTML abrufen Rendert die Teilnehmerinformationen des Angebots als direktes HTML-Dokument. Die Pfad-ID wählt das Angebot. Die Ausgabe kann personenbezogene Teilnehmerdaten enthalten; sie erzeugt keine Buchung. language im Header hat Vorrang vor dem Query-Parameter lang. - Operation-ID: get_v2_offer_id_participant_information_html - Dokumentation: /docs/api/v2/operations/get-v2-offer-id-participant-information-html-de395833b5aa/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | lang | query | nein | Ausgabesprache, falls der language-Header fehlt. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"lang","in":"query","required":false,"description":"Ausgabesprache, falls der language-Header fehlt.","schema":{"type":"string"},"example":"de"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/participant_information.html' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/participant_information.html"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/participant_information.html" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_id_participant_information_html","summary":"Teilnehmerinformationen als HTML abrufen","description":"Rendert die Teilnehmerinformationen des Angebots als direktes HTML-Dokument. Die Pfad-ID wählt das Angebot. Die Ausgabe kann personenbezogene Teilnehmerdaten enthalten; sie erzeugt keine Buchung. language im Header hat Vorrang vor dem Query-Parameter lang.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"lang","in":"query","required":false,"description":"Ausgabesprache, falls der language-Header fehlt.","schema":{"type":"string"},"example":"de"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Gerendertes HTML-Dokument mit Teilnehmerdaten.","content":{"text/html":{"schema":{"type":"string"},"example":"

Beispielangebot

Teilnehmer
Beispielperson
"}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1344}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/{id}/questiongroup Zusatzfragengruppe einem Angebot zuordnen Fügt die Zusatzfragengruppe zum Angebot hinzu oder entfernt die Zuordnung. change=update ändert die Sortierung; sort wird numerisch normalisiert und ist optional (Standard 0). Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_id_questiongroup - Dokumentation: /docs/api/v2/operations/put-v2-offer-id-questiongroup-fc442a41da0c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "questiongroup": "questiongroup_012345abcdef_123456", "change": "add", "sort": 0 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/questiongroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/questiongroup"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "questiongroup": "questiongroup_012345abcdef_123456", "change": "add", "sort": 0 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/questiongroup" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"questiongroup\":\"questiongroup_012345abcdef_123456\",\"change\":\"add\",\"sort\":0}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_id_questiongroup","summary":"Zusatzfragengruppe einem Angebot zuordnen","description":"Fügt die Zusatzfragengruppe zum Angebot hinzu oder entfernt die Zuordnung. change=update ändert die Sortierung; sort wird numerisch normalisiert und ist optional (Standard 0). Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"questiongroup":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"change":{"type":"string","enum":["add","remove","update"]},"sort":{"x-edoobox-canonicalType":"integer","description":"PHP-Integernormalisierung; Standard 0, keine strikte JSON-Typprüfung."}},"additionalProperties":true,"required":["questiongroup","change"]},"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zusatzfragengruppe einem Angebot zuordnen","value":{"data":{"questiongroup":"questiongroup_012345abcdef_123456","change":"add","sort":0}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":828}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/{id}/shareurl Freigabe- und Buchungslinks für Angebot abrufen Ermittelt die vorhandenen Buchungs-, Einbettungs- und Trackinglinks sowie den internen Zugangscode. Bei Kindern eines Multi-Angebots beziehen sich die Links auf das übergeordnete Multi-Angebot; data.id bleibt die angefragte ID. Die Operation erstellt keine Buchung. Die Pfad-ID wird als id-Filter ausgewertet. - Operation-ID: get_v2_offer_id_shareurl - Dokumentation: /docs/api/v2/operations/get-v2-offer-id-shareurl-82b4a3ca012c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | Optional; die Pfad-ID wird zuerst eingesetzt. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"Optional; die Pfad-ID wird zuerst eingesetzt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/shareurl' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/shareurl"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/shareurl" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_id_shareurl","summary":"Freigabe- und Buchungslinks für Angebot abrufen","description":"Ermittelt die vorhandenen Buchungs-, Einbettungs- und Trackinglinks sowie den internen Zugangscode. Bei Kindern eines Multi-Angebots beziehen sich die Links auf das übergeordnete Multi-Angebot; data.id bleibt die angefragte ID. Die Operation erstellt keine Buchung. Die Pfad-ID wird als id-Filter ausgewertet.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"Optional; die Pfad-ID wird zuerst eingesetzt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","shareurl":{"internal_code":"","url":{"base":"https://app.edoobox.com/","booknow":"https://example.invalid/book","segment":"beispiel","gtm":"https://example.invalid/tracking","iframe":"","shortcut":"beispiel"},"data":{"iframe":false}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"shareurl":{"type":"object","properties":{"internal_code":{"type":"string"},"url":{"type":"object","properties":{"base":{"type":"string"},"booknow":{"type":"string"},"segment":{"type":"string"},"gtm":{"type":"string"},"iframe":{"type":"string"},"shortcut":{"type":"string"}},"additionalProperties":true},"data":{"type":"object","properties":{"iframe":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true,"required":["id","shareurl"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Offer.php","line":1324,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/12","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Offer.php","line":1324,"reason":"Exact shareurl GET calls Offerfunc._getShareWinUrl."},"reason":"Read share URLs for an owned offer, composing encrypted booking routes and existing language/category slugs.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/12","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1324}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/{id}/trash_permission Papierkorbfreigabe für ein Angebot prüfen Prüft ohne Änderung, ob das Angebot nach aktuellen Status- und Multi-Angebotsregeln in den Papierkorb verschoben werden darf. allowed=false liefert einen maschinenlesbaren reason; die spätere Verschiebung prüft den Zustand erneut. - Operation-ID: get_v2_offer_id_trash_permission - Dokumentation: /docs/api/v2/operations/get-v2-offer-id-trash-permission-0b66c78bc367/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/'"${PATH_ID}"'/trash_permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/{id}/trash_permission"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/{id}/trash_permission" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_id_trash_permission","summary":"Papierkorbfreigabe für ein Angebot prüfen","description":"Prüft ohne Änderung, ob das Angebot nach aktuellen Status- und Multi-Angebotsregeln in den Papierkorb verschoben werden darf. allowed=false liefert einen maschinenlesbaren reason; die spätere Verschiebung prüft den Zustand erneut.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","allowed":true,"reason":null}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"allowed":{"type":"boolean"},"reason":{"type":["string","null"]}},"additionalProperties":true,"required":["id","allowed","reason"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/13","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Offer.php","line":1021,"reason":"Exact trash_permission GET selects getTrashPermission, not the trash mutation."},"reason":"Calculate whether an offer could be moved to trash, without trashing it.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/13","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1021}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list Angebote auflisten Liest Angebote im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert Angebote ohne endgültig gelöschte Einträge; Archiv- oder Papierkorbeinträge sind ohne entsprechende Filter nicht automatisch ausgeschlossen. Über fields können zusätzlich hasDate, shareurl, internal_code und calendar_week angefordert werden; admin ist ein zusätzlicher Filter auf Terminleitungen. - Operation-ID: get_v2_offer_list - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-f8ea4c8def99/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list","summary":"Angebote auflisten","description":"Liest Angebote im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert Angebote ohne endgültig gelöschte Einträge; Archiv- oder Papierkorbeinträge sind ohne entsprechende Filter nicht automatisch ausgeschlossen. Über fields können zusätzlich hasDate, shareurl, internal_code und calendar_week angefordert werden; admin ist ein zusätzlicher Filter auf Terminleitungen.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offer_012345abcdef_123456":{"id":"offer_012345abcdef_123456","name":"Beispielangebot","number":"BEISPIEL-2026","status":0,"type":"offer","category":"category_012345abcdef_123456","user_maximum":20,"hasDate":true,"calendar_week":45}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string"},"hasDate":{"type":"boolean"},"shareurl":{"type":"string"},"calendar_week":{"type":"integer"},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string"},"hasDate":{"type":"boolean"},"shareurl":{"type":"string"},"calendar_week":{"type":"integer"},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true}}],"description":"ID-indizierte Sammlung; response_type=array liefert eine Liste. Leere Ergebnismengen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/export Angebote für einen Zeitraum exportieren Erstellt einen Angebotsgesamt-Export im gewählten Konto. from und to müssen mit PHP strtotime parsebare Zeitwerte sein und to muss nach from liegen; format wählt csv, xls, xlsx oder ods. Es werden Angebote mit Angebotsbeginn strikt nach from und strikt vor to berücksichtigt; dies ist keine Überschneidungssuche über Beginn und Ende. filter, fields und die generische Listenpaginierung werden für diesen Export nicht verwendet. - Operation-ID: get_v2_offer_list_export - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-export-d25e2e49bf7b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | from | query | ja | Parsebarer Zeitwert; nur Angebote mit Beginn strikt nach from werden exportiert. | | to | query | ja | Parsebarer Zeitwert nach from; nur Angebote mit Beginn strikt vor to werden exportiert. | | format | query | ja | Format der erzeugten Exportdatei. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"from","in":"query","required":true,"description":"Parsebarer Zeitwert; nur Angebote mit Beginn strikt nach from werden exportiert.","schema":{"type":"string"},"example":"2026-11-01T00:00:00+01:00"},{"name":"to","in":"query","required":true,"description":"Parsebarer Zeitwert nach from; nur Angebote mit Beginn strikt vor to werden exportiert.","schema":{"type":"string"},"example":"2026-12-01T00:00:00+01:00"},{"name":"format","in":"query","required":true,"description":"Format der erzeugten Exportdatei.","schema":{"type":"string","enum":["csv","xls","xlsx","ods"]},"example":"csv"}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FROM:?QUERY_FROM setzen}" : "${QUERY_TO:?QUERY_TO setzen}" : "${QUERY_FORMAT:?QUERY_FORMAT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/export' \ --url-query 'from='"${QUERY_FROM}" \ --url-query 'to='"${QUERY_TO}" \ --url-query 'format='"${QUERY_FORMAT}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/export"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("from", required("QUERY_FROM")); url.searchParams.append("to", required("QUERY_TO")); url.searchParams.append("format", required("QUERY_FORMAT")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/export" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("from", os.environ["QUERY_FROM"]), ("to", os.environ["QUERY_TO"]), ("format", os.environ["QUERY_FORMAT"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_export","summary":"Angebote für einen Zeitraum exportieren","description":"Erstellt einen Angebotsgesamt-Export im gewählten Konto. from und to müssen mit PHP strtotime parsebare Zeitwerte sein und to muss nach from liegen; format wählt csv, xls, xlsx oder ods. Es werden Angebote mit Angebotsbeginn strikt nach from und strikt vor to berücksichtigt; dies ist keine Überschneidungssuche über Beginn und Ende. filter, fields und die generische Listenpaginierung werden für diesen Export nicht verwendet.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"from","in":"query","required":true,"description":"Parsebarer Zeitwert; nur Angebote mit Beginn strikt nach from werden exportiert.","schema":{"type":"string"},"example":"2026-11-01T00:00:00+01:00"},{"name":"to","in":"query","required":true,"description":"Parsebarer Zeitwert nach from; nur Angebote mit Beginn strikt vor to werden exportiert.","schema":{"type":"string"},"example":"2026-12-01T00:00:00+01:00"},{"name":"format","in":"query","required":true,"description":"Format der erzeugten Exportdatei.","schema":{"type":"string","enum":["csv","xls","xlsx","ods"]},"example":"csv"}],"responses":{"200":{"description":"Generated spreadsheet download. CSV output can include a UTF-8 BOM.","headers":{"Content-Disposition":{"schema":{"type":"string"},"example":"attachment; filename=\"Export.csv\""}},"content":{"text/csv":{"schema":{"type":"string"},"example":"Name;Number\nExample;EXAMPLE-2026\n"},"application/vnd.ms-excel":{"schema":{"type":"string","format":"binary"}},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"schema":{"type":"string","format":"binary"}},"application/vnd.oasis.opendocument.spreadsheet":{"schema":{"type":"string","format":"binary"}}}},"402":{"description":"Missing/invalid from, to, or format; response require map identifies invalid fields.","content":{"application/json":{"example":{"success":false,"error":"ED80100","require":{"from":false,"to":false,"format":false}}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Allowed formats are streamed to php://output; persistent xlsxFile mode is not accepted here."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/export1.csv Teilnehmerexport 1 als CSV herunterladen Erzeugt den Teilnehmer-/Buchungsexport 1 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten. - Operation-ID: get_v2_offer_list_export1_csv - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-export1-csv-98b789bd277a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/export1.csv' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/export1.csv"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/export1.csv" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_export1_csv","summary":"Teilnehmerexport 1 als CSV herunterladen","description":"Erzeugt den Teilnehmer-/Buchungsexport 1 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generierter Dateidownload; die Spalten werden vom Konto und Exporttyp bestimmt.","headers":{"Content-Disposition":{"schema":{"type":"string"},"example":"attachment; filename=\"Beispielexport.csv\""}},"content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Allowed formats are streamed to php://output; persistent xlsxFile mode is not accepted here."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1332}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/export1.ods Teilnehmerexport 1 als ODS herunterladen Erzeugt den Teilnehmer-/Buchungsexport 1 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten. - Operation-ID: get_v2_offer_list_export1_ods - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-export1-ods-f76479ca1426/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/export1.ods' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/export1.ods"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/export1.ods" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_export1_ods","summary":"Teilnehmerexport 1 als ODS herunterladen","description":"Erzeugt den Teilnehmer-/Buchungsexport 1 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generierter Dateidownload; die Spalten werden vom Konto und Exporttyp bestimmt.","headers":{"Content-Disposition":{"schema":{"type":"string"},"example":"attachment; filename=\"Beispielexport.ods\""}},"content":{"application/vnd.oasis.opendocument.spreadsheet":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Allowed formats are streamed to php://output; persistent xlsxFile mode is not accepted here."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1333}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/export1.xlsx Teilnehmerexport 1 als XLSX herunterladen Erzeugt den Teilnehmer-/Buchungsexport 1 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten. - Operation-ID: get_v2_offer_list_export1_xlsx - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-export1-xlsx-9aace58599e1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/export1.xlsx' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/export1.xlsx"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/export1.xlsx" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_export1_xlsx","summary":"Teilnehmerexport 1 als XLSX herunterladen","description":"Erzeugt den Teilnehmer-/Buchungsexport 1 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generierter Dateidownload; die Spalten werden vom Konto und Exporttyp bestimmt.","headers":{"Content-Disposition":{"schema":{"type":"string"},"example":"attachment; filename=\"Beispielexport.xlsx\""}},"content":{"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Allowed formats are streamed to php://output; persistent xlsxFile mode is not accepted here."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1331}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/export2.csv Teilnehmerexport 2 als CSV herunterladen Erzeugt den Teilnehmer-/Buchungsexport 2 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten. - Operation-ID: get_v2_offer_list_export2_csv - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-export2-csv-337c780394d1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/export2.csv' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/export2.csv"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/export2.csv" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_export2_csv","summary":"Teilnehmerexport 2 als CSV herunterladen","description":"Erzeugt den Teilnehmer-/Buchungsexport 2 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generierter Dateidownload; die Spalten werden vom Konto und Exporttyp bestimmt.","headers":{"Content-Disposition":{"schema":{"type":"string"},"example":"attachment; filename=\"Beispielexport.csv\""}},"content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Allowed formats are streamed to php://output; persistent xlsxFile mode is not accepted here."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1335}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/export2.ods Teilnehmerexport 2 als ODS herunterladen Erzeugt den Teilnehmer-/Buchungsexport 2 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten. - Operation-ID: get_v2_offer_list_export2_ods - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-export2-ods-be529c937f87/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/export2.ods' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/export2.ods"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/export2.ods" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_export2_ods","summary":"Teilnehmerexport 2 als ODS herunterladen","description":"Erzeugt den Teilnehmer-/Buchungsexport 2 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generierter Dateidownload; die Spalten werden vom Konto und Exporttyp bestimmt.","headers":{"Content-Disposition":{"schema":{"type":"string"},"example":"attachment; filename=\"Beispielexport.ods\""}},"content":{"application/vnd.oasis.opendocument.spreadsheet":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Allowed formats are streamed to php://output; persistent xlsxFile mode is not accepted here."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1336}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/export2.xlsx Teilnehmerexport 2 als XLSX herunterladen Erzeugt den Teilnehmer-/Buchungsexport 2 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten. - Operation-ID: get_v2_offer_list_export2_xlsx - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-export2-xlsx-bc0334fd036f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/export2.xlsx' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/export2.xlsx"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/export2.xlsx" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_export2_xlsx","summary":"Teilnehmerexport 2 als XLSX herunterladen","description":"Erzeugt den Teilnehmer-/Buchungsexport 2 für genau ein Angebot. filter muss einen id-Eintrag mit öffentlicher offer-ID enthalten. Das Format ergibt sich aus dem Dateisuffix; from, to und format werden hier nicht verwendet. Spalten hängen unter anderem von Kontofeldern, freier Terminauswahl und Multi-Angeboten ab. Der Download kann personenbezogene Teilnehmerdaten enthalten.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste mit einer einzelnen öffentlichen offer-ID unter property=id.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generierter Dateidownload; die Spalten werden vom Konto und Exporttyp bestimmt.","headers":{"Content-Disposition":{"schema":{"type":"string"},"example":"attachment; filename=\"Beispielexport.xlsx\""}},"content":{"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/libraries/edoobox/integrations/export/Export.php","line":127},"reason":"Allowed formats are streamed to php://output; persistent xlsxFile mode is not accepted here."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1334}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/offer/list/files Datei zu einer Ressource hochladen Lädt eine Datei hoch und ordnet sie der über meta angegebenen kontoeigenen Ressource zu. Multipart-Teil file enthält die Bytes; meta ist ein JSON-String {data:{resource_type,resource_id,filetype,...}}. resource_id muss exakt die zum resource_type passende öffentliche offer-, user- oder admin-ID sein. Für Angebotsdateien: internalOfferFile (geschützt) oder externalOfferFile (öffentlich), jeweils bis 20 MiB. Weitere Typen: avatar (8 MiB, JPEG/PNG/WebP), sign und image2/image3 (2 MiB, JPEG/PNG/GIF/WebP). image1 ist im Getter vorhanden, wird vom Upload-Hook aber abgelehnt. Avatare und Signaturen ersetzen bestehende gleichartige Einträge. Dateityp-, Eigentümer- und Kontoprüfungen gelten vor dem Schreiben. - Operation-ID: post_v2_offer_list_files - Dokumentation: /docs/api/v2/operations/post-v2-offer-list-files-8c0d9c35a40e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/offer/list/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/files"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/offer/list/files" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_offer_list_files","summary":"Datei zu einer Ressource hochladen","description":"Lädt eine Datei hoch und ordnet sie der über meta angegebenen kontoeigenen Ressource zu. Multipart-Teil file enthält die Bytes; meta ist ein JSON-String {data:{resource_type,resource_id,filetype,...}}. resource_id muss exakt die zum resource_type passende öffentliche offer-, user- oder admin-ID sein. Für Angebotsdateien: internalOfferFile (geschützt) oder externalOfferFile (öffentlich), jeweils bis 20 MiB. Weitere Typen: avatar (8 MiB, JPEG/PNG/WebP), sign und image2/image3 (2 MiB, JPEG/PNG/GIF/WebP). image1 ist im Getter vorhanden, wird vom Upload-Hook aber abgelehnt. Avatare und Signaturen ersetzen bestehende gleichartige Einträge. Dateityp-, Eigentümer- und Kontoprüfungen gelten vor dem Schreiben.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","meta"],"properties":{"file":{"type":"string","format":"binary","description":"Uploaded file bytes; enforce server MIME and size limits."},"meta":{"type":"string","contentMediaType":"application/json","description":"JSON-Umschlag mit data.resource_type, data.resource_id und data.filetype als Pflichtangaben; filename und sprachcodiertes description sind optional. Die Ressource muss im Konto bestehen. Beispiel synthetisch.","examples":["{\"data\":{\"resource_type\":\"offer\",\"resource_id\":\"offer_012345abcdef_123456\",\"filetype\":\"externalOfferFile\",\"filename\":\"example.pdf\"}}"],"contentSchema":{"type":"object","properties":{"data":{"type":"object","properties":{"resource_type":{"type":"string","enum":["offer","user","admin"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]}]},"filetype":{"type":"string","enum":["avatar","sign","internalOfferFile","externalOfferFile","image2","image3"]},"filename":{"type":"string"},"description":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true,"required":["resource_type","resource_id","filetype"]}},"additionalProperties":true,"required":["data"]}}},"additionalProperties":false},"examples":{"upload":{"summary":"Illustrative multipart parts; supply actual file bytes.","value":{"file":"","meta":"{\"data\":{\"resource_type\":\"offer\",\"resource_id\":\"offer_012345abcdef_123456\",\"filetype\":\"externalOfferFile\",\"filename\":\"example.pdf\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"postOffer","line":681,"branch":{"file":"application/models/v2/Offer.php","line":691}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/files/{fileId}/download Datei einer Ressource herunterladen Lädt die Bytes einer kontoeigenen, nicht gelöschten Ressourcendatei anhand der öffentlichen files-ID herunter. Der Dateiinhalt bestimmt den MIME-Typ und den Downloadnamen. Kundenspezifische Exporte sind hier absichtlich nicht abrufbar und benötigen ihren eigenen Download-Endpunkt. Fehlende, fremde oder gesperrte Dateien liefern file_not_found. - Operation-ID: get_v2_offer_list_files_fileId_download - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-files-fileid-download-f09177ad2886/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fileId | path | ja | Öffentliche files-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fileId","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_FILEID:?PATH_FILEID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/files/'"${PATH_FILEID}"'/download' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/files/{fileId}/download"; path = path.replaceAll("{fileId}", encodeURIComponent(required("PATH_FILEID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/files/{fileId}/download" path = path.replace("{fileId}", urllib.parse.quote(os.environ["PATH_FILEID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_files_fileId_download","summary":"Datei einer Ressource herunterladen","description":"Lädt die Bytes einer kontoeigenen, nicht gelöschten Ressourcendatei anhand der öffentlichen files-ID herunter. Der Dateiinhalt bestimmt den MIME-Typ und den Downloadnamen. Kundenspezifische Exporte sind hier absichtlich nicht abrufbar und benötigen ihren eigenen Download-Endpunkt. Fehlende, fremde oder gesperrte Dateien liefern file_not_found.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fileId","in":"path","required":true,"description":"Öffentliche files-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Authorized resource-file download. The file determines MIME type.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/files/list Angebotsdateien auflisten Liest die Metadaten von Dateien mit resource_type=offer. Gelöschte Dateien sowie kundenspezifische Exportdateien werden ausgeschlossen. Mit filter auf resource_id (öffentliche offer-ID) kann auf ein Angebot eingeschränkt werden. - Operation-ID: get_v2_offer_list_files_list - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-files-list-256519813dcc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/files/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/files/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/files/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_files_list","summary":"Angebotsdateien auflisten","description":"Liest die Metadaten von Dateien mit resource_type=offer. Gelöschte Dateien sowie kundenspezifische Exportdateien werden ausgeschlossen. Mit filter auf resource_id (öffentliche offer-ID) kann auf ein Angebot eingeschränkt werden.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"files_012345abcdef_123456":{"id":"files_012345abcdef_123456","filename":"beispiel.pdf","description":{"de":"Kursunterlagen"},"filetype":"externalOfferFile","resource_type":"offer","resource_id":"offer_012345abcdef_123456","last_change":"2026-10-22T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","items":{"type":"string"}}]},"filetype":{"type":"string"},"resource_type":{"type":"string","const":"offer"},"resource_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"last_change":{"type":"string"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","items":{"type":"string"}}]},"filetype":{"type":"string"},"resource_type":{"type":"string","const":"offer"},"resource_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"last_change":{"type":"string"}},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Files.php","method":"getFiles","line":245},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/globaltable Angebotsübersicht mit Belegungsdaten abrufen Liefert die Angebotsübersicht mit Kategoriebezeichnung, öffentlicher Sichtbarkeit, Kalenderwoche, Leitungen, Räumen, Orten und Zählern für Buchungen, Warteliste und offene Anfragen. fields steuert die Feldauswahl; total_revenue wird nur bei expliziter Anforderung ergänzt. Der spezielle Filter property=offer_tags mit expression=containsAll berücksichtigt Angebote, die alle gewählten normalen Angebotstags besitzen. value ist eine Liste öffentlicher tag-IDs oder eine kommagetrennte Zeichenfolge; höchstens vier gültige eindeutige Tags werden berücksichtigt. Ohne gültigen Tag ergibt dieser Filter keine Treffer. Es werden keine Angebote oder Buchungen geändert. - Operation-ID: get_v2_offer_list_globaltable - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-globaltable-7bd6eb9a04cd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | Generische Filter; zusätzlich offer_tags/containsAll mit maximal vier normalen Angebotstags. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Generische Filter; zusätzlich offer_tags/containsAll mit maximal vier normalen Angebotstags.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true}},"example":[{"property":"offer_tags","expression":"containsAll","value":["tag_012345abcdef_123456"]}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/globaltable' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/globaltable"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/globaltable" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_globaltable","summary":"Angebotsübersicht mit Belegungsdaten abrufen","description":"Liefert die Angebotsübersicht mit Kategoriebezeichnung, öffentlicher Sichtbarkeit, Kalenderwoche, Leitungen, Räumen, Orten und Zählern für Buchungen, Warteliste und offene Anfragen. fields steuert die Feldauswahl; total_revenue wird nur bei expliziter Anforderung ergänzt. Der spezielle Filter property=offer_tags mit expression=containsAll berücksichtigt Angebote, die alle gewählten normalen Angebotstags besitzen. value ist eine Liste öffentlicher tag-IDs oder eine kommagetrennte Zeichenfolge; höchstens vier gültige eindeutige Tags werden berücksichtigt. Ohne gültigen Tag ergibt dieser Filter keine Treffer. Es werden keine Angebote oder Buchungen geändert.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Generische Filter; zusätzlich offer_tags/containsAll mit maximal vier normalen Angebotstags.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true}},"example":[{"property":"offer_tags","expression":"containsAll","value":["tag_012345abcdef_123456"]}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offer_012345abcdef_123456":{"id":"offer_012345abcdef_123456","name":"Beispielangebot","publicly_visible":true,"category_name":"Weiterbildung","calendar_week":45,"count_booking":8,"count_waitinglist":0,"count_offerlist":1,"leaders":[],"offer_rooms":[],"offer_places":[]}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"publicly_visible":{"type":"boolean"},"calendar_week":{"type":"integer"},"shareurl":{"type":"string"},"category_name":{"type":"string"},"leaders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true}},"offer_rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"offer_places":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"count_booking":{"type":"integer"},"count_waitinglist":{"type":"integer"},"count_offerlist":{"type":"integer"},"total_revenue":{"type":"number"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":269,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"publicly_visible":{"type":"boolean"},"calendar_week":{"type":"integer"},"shareurl":{"type":"string"},"category_name":{"type":"string"},"leaders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true}},"offer_rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"offer_places":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"}},"additionalProperties":true}},"count_booking":{"type":"integer"},"count_waitinglist":{"type":"integer"},"count_offerlist":{"type":"integer"},"total_revenue":{"type":"number"}},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/10","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Offer.php","line":1351,"reason":"Exact globaltable branch delegates current Offer Structure to Offersglobaltable."},"reason":"Read global offer table with existing leaders/rooms/places, counts, calendar week and optional revenue aggregates.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/10","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1351}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/offer/list/ical_feed Angebot über den Legacy-Pfad anlegen Dieser Legacy-Pfad wird von Offer.postOffer als normale Angebotserstellung verarbeitet. Er erzeugt keinen Kalenderfeed. Für eine Feed-URL die POST-Operationen unter date/.../ical_feed verwenden. Die Pflichtfelder und Baum-/Abo-Regeln entsprechen POST /v2/offer. - Operation-ID: post_v2_offer_list_ical_feed - Dokumentation: /docs/api/v2/operations/post-v2-offer-list-ical-feed-b9e056f98ad0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Beispielangebot", "category": "category_012345abcdef_123456", "type": "offer" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/offer/list/ical_feed' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/ical_feed"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Beispielangebot", "category": "category_012345abcdef_123456", "type": "offer" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/ical_feed" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Beispielangebot\",\"category\":\"category_012345abcdef_123456\",\"type\":\"offer\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_offer_list_ical_feed","summary":"Angebot über den Legacy-Pfad anlegen","description":"Dieser Legacy-Pfad wird von Offer.postOffer als normale Angebotserstellung verarbeitet. Er erzeugt keinen Kalenderfeed. Für eine Feed-URL die POST-Operationen unter date/.../ical_feed verwenden. Die Pflichtfelder und Baum-/Abo-Regeln entsprechen POST /v2/offer.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"name":{"anyOf":[{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":31},{"type":"array","items":{"type":"object","required":["language","value"],"properties":{"language":{"type":"string"},"value":{"type":"string","maxLength":80}},"additionalProperties":true}}],"description":"Offer::postOffer also accepts localized name entries, selecting the account default language."},"number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":59},"row_lft":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":68,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_rgt":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":78,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"user_maximum":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":88,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":104,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"user_minimal":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":122,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":137},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":147},"date_close":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":157},"date_displayend_reference":{"x-edoobox-knownInputs":["date_start","date_end","date_close"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["date_start"],"x-edoobox-sourceLine":176,"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":203,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_displaystart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":218},"date_signupstart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":227},"image":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":256},"category":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":269},"offerdef":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":293},"type":{"x-edoobox-knownInputs":["offer","folder","archive","trash"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":308},"mode":{"enum":["Online","Onsite","Blended"],"x-edoobox-sourceLine":347},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":365,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"archive":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":380,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"trash":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":392,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"waiting_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":404,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mustpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":413,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"countdown":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":422,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"lesson_booking":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":437,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":449,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration_type":{"x-edoobox-knownInputs":["named_users","participant_count","named_users_contact"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["named_users"],"x-edoobox-sourceLine":459},"barcode_ticket":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":486,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":495},"multi_offer":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":508,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multi_offer_child":{"readOnly":true,"description":"Getter returns null without reading input. Before-insert derives child-parent relation from validated category/parent (kat_id) and overwrites it for multi-offer children. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":566},"shortdescription":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces normal/include/exclude tag associations. Every supplied tag must be a typed, existing same-account tag and every mode must be recognized before relation writes. Duplicate associations are normalized.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":40,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":640}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","category","type"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"name":{"anyOf":[{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":31},{"type":"array","items":{"type":"object","required":["language","value"],"properties":{"language":{"type":"string"},"value":{"type":"string","maxLength":80}},"additionalProperties":true}}],"description":"Offer::postOffer also accepts localized name entries, selecting the account default language."},"number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":59},"row_lft":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":68,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"row_rgt":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":78,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"user_maximum":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":88,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"order":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":104,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"user_minimal":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":122,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":137},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":147},"date_close":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":157},"date_displayend_reference":{"x-edoobox-knownInputs":["date_start","date_end","date_close"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["date_start"],"x-edoobox-sourceLine":176,"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":203,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_displaystart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":218},"date_signupstart":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":227},"image":{"not":{},"description":"No supplied value is accepted here: getter returns strict false and generic API_DATA rejects ED80100. Omit server-maintained field.\n\nGetter always returns strict false, regardless of supplied value.\n\nRuntime preconditions / rejection: Every supplied value on generic POST (and generic PUT when update is allowed) causes ED80100 through API_DATA strict-false check.\n\nScope / limitations: Recommend response-only/readOnly, remove from generic write properties; separate actions can manage it through their own payload logic.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":256},"category":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["category_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":269},"offerdef":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":293},"type":{"x-edoobox-knownInputs":["offer","folder","archive","trash"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":308},"mode":{"enum":["Online","Onsite","Blended"],"x-edoobox-sourceLine":347},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":365,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"archive":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":380,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"trash":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":392,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"waiting_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":404,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mustpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":413,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"countdown":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":422,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"lesson_booking":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":437,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":449,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multiple_registration_type":{"x-edoobox-knownInputs":["named_users","participant_count","named_users_contact"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["named_users"],"x-edoobox-sourceLine":459},"barcode_ticket":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":486,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":495},"multi_offer":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":508,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"multi_offer_child":{"readOnly":true,"description":"Getter returns null without reading input. Before-insert derives child-parent relation from validated category/parent (kat_id) and overwrites it for multi-offer children. ","x-edoobox-schemaStatus":"source-reviewed-not-writable","x-edoobox-clientRestriction":"Omit server-generated or unsupported input fields; runtime behavior is described separately.","x-edoobox-ignoredInput":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":566},"shortdescription":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces normal/include/exclude tag associations. Every supplied tag must be a typed, existing same-account tag and every mode must be recognized before relation writes. Duplicate associations are normalized.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":40,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":640}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","category","type"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Angebot über den Legacy-Pfad anlegen","value":{"data":{"name":"Beispielangebot","category":"category_012345abcdef_123456","type":"offer"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"postOffer","line":681,"branch":{"file":"application/models/v2/Date.php","line":195}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/shareurl Freigabe- und Buchungslinks für Angebot abrufen Ermittelt die vorhandenen Buchungs-, Einbettungs- und Trackinglinks sowie den internen Zugangscode. Bei Kindern eines Multi-Angebots beziehen sich die Links auf das übergeordnete Multi-Angebot; data.id bleibt die angefragte ID. Die Operation erstellt keine Buchung. filter muss einen Eintrag mit property=id und einer einzelnen öffentlichen offer-ID enthalten. - Operation-ID: get_v2_offer_list_shareurl - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-shareurl-98ea8266a366/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filterliste; ein einzelner id-Filter ist für diese Sammlung erforderlich. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste; ein einzelner id-Filter ist für diese Sammlung erforderlich.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/shareurl' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/shareurl"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/shareurl" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_shareurl","summary":"Freigabe- und Buchungslinks für Angebot abrufen","description":"Ermittelt die vorhandenen Buchungs-, Einbettungs- und Trackinglinks sowie den internen Zugangscode. Bei Kindern eines Multi-Angebots beziehen sich die Links auf das übergeordnete Multi-Angebot; data.id bleibt die angefragte ID. Die Operation erstellt keine Buchung. filter muss einen Eintrag mit property=id und einer einzelnen öffentlichen offer-ID enthalten.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filterliste; ein einzelner id-Filter ist für diese Sammlung erforderlich.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"expression":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"offer_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offer_012345abcdef_123456","shareurl":{"internal_code":"","url":{"base":"https://app.edoobox.com/","booknow":"https://example.invalid/book","segment":"beispiel","gtm":"https://example.invalid/tracking","iframe":"","shortcut":"beispiel"},"data":{"iframe":false}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"shareurl":{"type":"object","properties":{"internal_code":{"type":"string"},"url":{"type":"object","properties":{"base":{"type":"string"},"booknow":{"type":"string"},"segment":{"type":"string"},"gtm":{"type":"string"},"iframe":{"type":"string"},"shortcut":{"type":"string"}},"additionalProperties":true},"data":{"type":"object","properties":{"iframe":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true,"required":["id","shareurl"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1324}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offer/list/tree Gesamten Angebots- und Kategorienbaum abrufen Liefert Angebote, Kategorieordner sowie aktive, Archiv- und Papierkorbwurzeln gemeinsam mit ihren Baumkoordinaten. Nicht-Angebotsknoten besitzen nur die für ihren Typ vorgesehenen Felder. reply wird intern auf 50000 gesetzt. Wenn ein Ordner mit row_lft=row_rgt=0 gefunden wird, kann der Aufruf fehlerhafte Archiv-Elternbeziehungen reparieren und den Archivbaum neu aufbauen; danach wird die Abfrage einmal wiederholt. Dieser GET kann deshalb gespeicherte Daten verändern. - Operation-ID: get_v2_offer_list_tree - Dokumentation: /docs/api/v2/operations/get-v2-offer-list-tree-aacdc0cc2154/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offer/list/tree' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/list/tree"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/list/tree" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offer_list_tree","summary":"Gesamten Angebots- und Kategorienbaum abrufen","description":"Liefert Angebote, Kategorieordner sowie aktive, Archiv- und Papierkorbwurzeln gemeinsam mit ihren Baumkoordinaten. Nicht-Angebotsknoten besitzen nur die für ihren Typ vorgesehenen Felder. reply wird intern auf 50000 gesetzt. Wenn ein Ordner mit row_lft=row_rgt=0 gefunden wird, kann der Aufruf fehlerhafte Archiv-Elternbeziehungen reparieren und den Archivbaum neu aufbauen; danach wird die Abfrage einmal wiederholt. Dieser GET kann deshalb gespeicherte Daten verändern.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offer_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"category_012345abcdef_123456":{"id":"category_012345abcdef_123456","name":"Beispielkategorie","type":"folder","row_lft":2,"row_rgt":5},"offer_012345abcdef_123456":{"id":"offer_012345abcdef_123456","name":"Beispielangebot","type":"offer","category":"category_012345abcdef_123456","row_lft":3,"row_rgt":4}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},{"const":false}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"calendar_week":{"type":"integer"},"calendar_week_iso":{"type":"integer"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"row_lft":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":68,"type":"integer"},"row_rgt":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"integer"},"user_maximum":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":88,"lineStart":88,"lineEnd":103,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"ef67ec9958d0589d12df524ed964d96f3647cf8176d2f107424bbb6d2715621d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":72,"lineStart":72,"lineEnd":85,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b4cc2c675e48a2ddfd8b73a207e8b793140887e1a00f7f3dd8dc5e05ddabbe0e","reason":"func delegates to _change_user_maximum_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":21,"lineStart":21,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"0a04bfb4c037dc7bd950da50fcbd419c47001d5879a737dff29c10afc183c998","reason":"Account-specific maximum checked after clamp."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"user_minimal":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"type":"integer"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147,"anyOf":[{"type":"string"},{"const":false}]},"date_close":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":157,"anyOf":[{"type":"string"},{"const":false}]},"date_displayend_reference":{"enum":["date_close","date_start","date_end"],"description":"'date_open' => array(\n'db' => $this->tableS.'anmeldeschluss',\n'get' => function ($a, $fid, $insert = false) {\nreturn dP($a);\n},\n'set' => function ($a) {\nreturn dQ($a);\n},\n),"},"date_displayend":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":203,"type":"integer"},"date_displaystart":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":218,"anyOf":[{"type":"string"},{"const":false}]},"date_signupstart":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":227,"lineStart":227,"lineEnd":236,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"a2dbd62e363a822b38cdbc9058263e13bd7e92f70f6fd200e7e4d7435d24b610","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":99,"lineStart":99,"lineEnd":113,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"19794aa7b608b508b4b2987afbce43721e8217056649de45174b7bc3a900507c","reason":"func delegates to _change_date_signupstart_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11613,"lineStart":11613,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"29d94d04d092603b477c949772b36e49a438b1550130b4ba4194c882983d8eed","reason":"dP parse/fallback and dQ date-or-false output."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating after successful positive parse."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"image":{"anyOf":[{"const":false},{"type":"object","properties":{"type":{"const":"file"},"name":{"type":"string"},"url":{"type":"string"},"url300":{"type":"string"}},"required":["type","name","url","url300"],"additionalProperties":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":238,"lineStart":238,"lineEnd":255,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"0910fe4d4b73261614b59c8bbd239974b4452748f0ecac01e90484f4471bcf8d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":256,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"category":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["category_012345abcdef_123456"]},{"const":false}]},"offerdef":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":293,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"type":{"enum":["offer","folder","root","archive","trash"]},"mode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":347},"status":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":365,"lineStart":365,"lineEnd":379,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"d763fc061eef55129445d0ce78b398e566748b018764270c35d41f4bac19281b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":981,"lineStart":981,"lineEnd":1094,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"b1caf7273b4abd50f432241a4d6ce8f321c70d7eb6a035aa17c88f6ce8e4ab01","reason":"func delegates to statusOffer"},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"archive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":380,"type":"boolean"},"trash":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":392,"type":"boolean"},"waiting_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":404,"type":"boolean"},"mustpay":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":413,"type":"boolean"},"countdown":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":422},"lesson_booking":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":437,"lineStart":437,"lineEnd":448,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"eda0ddf56bc7ae84e33f342f55baa52a6b54e1bd0241bbc4b19927e8d7db3a42","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":11,"lineStart":11,"lineEnd":58,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"2982272dd02cad7bd8dddc88f941f504b13f74048f866a69ceb2e309ef376c67","reason":"func delegates to change_lessenbooking_Offer"},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Feature gating on enabling."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"multiple_registration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":449,"type":"boolean"},"multiple_registration_type":{"enum":["named_users","participant_count","named_users_contact"]},"barcode_ticket":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":486,"type":"boolean"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":495,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"multi_offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":508,"type":"boolean"},"multi_offer_child":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":519,"lineStart":519,"lineEnd":531,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"f650845f3d4ac52e175c569575d10fb6316589d16d687a37890e060fe7c41384","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":1901,"lineStart":1901,"lineEnd":1980,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"d5dbd63221aa637fb979245ed9ecd7be3623ba73b35db42c3a96257be12741d5","reason":"Locked parent/lifecycle checks and assignment from kat_id."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"vat":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":false},{"const":"dynamic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":534,"reason":"Input accepts the dynamic sentinel before decoding a vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":540,"reason":"Response returns false, dynamic or a public vat ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"cancellation_condition_group":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":0}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":552,"reason":"Write decodes a public cancellationconditiongroup ID and rejects nonpositive results.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offer.php","line":559,"reason":"Response uses integer zero when no group is assigned.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"}]},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":566,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortdescription":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":579},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerfunc.php","line":2534,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d"},{"file":"application/models/v2/Offer.php","line":605,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5"},{"file":"application/models/v2/Offerfunc.php","line":2557,"lineEnd":2578,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","reason":"All supplied tag IDs/types and the complete same-account tag existence set are validated before offer tag relation writes; unknown modes are rejected."}]},"internal_code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Offer.php","line":640,"lineStart":640,"lineEnd":649,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"7faaa8f4c54f5252216448518d9f957cb5a06ae067628a2e40462d6b4bec6c75","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerfunc.php","line":115,"lineStart":115,"lineEnd":152,"sha256":"0efa50df0d95a6e96901b30e045e68b43f00529f73fcccaf864d23d02c27e63d","spanSha256":"93f7e04f0ce93ea8b69acb8e721ca2f0ad32d3aec7bd79a4025bb2a920a8078a","reason":"func delegates to _change_internal_code_Offer"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization; no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Edsubscription/Edsubscriptionverification.php","line":45,"lineStart":45,"lineEnd":71,"sha256":"723114d7051c7917de61d4cc28f26acef144a985b6077ccaefb0052f4c4b78dd","spanSha256":"c9425ad19c85e68c6bc57eacc00cebd71d4e624b76013e116afd1cadcd87b7e8","reason":"Runtime subscription feature rejection."},{"file":"application/models/v2/Offer.php","line":681,"lineStart":681,"lineEnd":705,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"226a81690573780762b7f4f9d79c4b4e00cd8c6e0b85d3a51ca5600c85b98e05","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":734,"lineStart":734,"lineEnd":735,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"3df2a38722a4fef1389fcc1049532ef37f0e37cfea45b5e550c72e79d46687ae","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Offer.php","line":805,"lineStart":805,"lineEnd":843,"sha256":"abb1c24b2f0d1773a83a689708ab5d7db2a474d05122eb89050e9a941ba8efb5","spanSha256":"68bdbbf1d36aa5a89930b605bcdd68e1dd4dee314cba249bde700958ee6b6ae7","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"calendar_week":{"type":"integer"},"calendar_week_iso":{"type":"integer"}},"additionalProperties":true}}],"description":"Nach öffentlichen IDs indiziertes Objekt oder (bei response_type=array) Liste. Leere Sammlungen können [] sein."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Offer.php","line":1268,"reason":"Tree branch reads via getTreeRowsWithSignupArchiveRepair, not the global table read path."},"reason":"Read offer tree; malformed signup archive folders can trigger a bounded parent and nested-set repair.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/11","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/11","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"getOffer","line":1014,"branch":{"file":"application/models/v2/Offer.php","line":1170}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/offer/remove/files/{id} Ressourcendatei löschen Markiert die über ihre files-ID gewählte Datei als gelöscht (iamdelete). Die Pfad-ID ist keine Angebots-ID. Ein JSON-id kann die Ziel-ID überschreiben. Kundenspezifische Exportdateien sind über diese generische Route gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: delete_v2_offer_remove_files_id - Dokumentation: /docs/api/v2/operations/delete-v2-offer-remove-files-id-cec9d016c1d0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche files-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"description":"Öffentliche files-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "files_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/offer/remove/files/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/remove/files/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "files_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/remove/files/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"files_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_offer_remove_files_id","summary":"Ressourcendatei löschen","description":"Markiert die über ihre files-ID gewählte Datei als gelöscht (iamdelete). Die Pfad-ID ist keine Angebots-ID. Ein JSON-id kann die Ziel-ID überschreiben. Kundenspezifische Exportdateien sind über diese generische Route gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"description":"Öffentliche files-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Ressourcendatei löschen","value":{"id":"files_012345abcdef_123456"}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Files.php","line":226,"reason":"Files explicitly rewrites the update/remove third segment to the file ID from segment5; exact Vue resource-file transport."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"deleteOffer","line":855,"branch":{"file":"application/models/v2/Offer.php","line":861}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offer/update/files/{id} Dateimetadaten aktualisieren Ändert filename und die sprachcodierte description einer vorhandenen Datei. id ist eine files-ID, keine Angebots-ID. Dateibytes, resource_type, resource_id und filetype werden dadurch nicht geändert; kundenspezifische Exportdateien sind über diese Route gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offer_update_files_id - Dokumentation: /docs/api/v2/operations/put-v2-offer-update-files-id-6daff87b1bcc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche files-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"description":"Öffentliche files-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": { "de": "Aktualisierte Kursunterlagen" } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offer/update/files/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/update/files/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": { "de": "Aktualisierte Kursunterlagen" } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offer/update/files/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":{\"de\":\"Aktualisierte Kursunterlagen\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offer_update_files_id","summary":"Dateimetadaten aktualisieren","description":"Ändert filename und die sprachcodierte description einer vorhandenen Datei. id ist eine files-ID, keine Angebots-ID. Dateibytes, resource_type, resource_id und filetype werden dadurch nicht geändert; kundenspezifische Exportdateien sind über diese Route gesperrt. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"description":"Öffentliche files-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"filename":{"type":"string"},"description":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true},"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dateimetadaten aktualisieren","value":{"data":{"description":{"de":"Aktualisierte Kursunterlagen"}}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Files.php","line":199,"reason":"Files explicitly rewrites the update/remove third segment to the file ID from segment5; exact Vue resource-file transport."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"updateOffer","line":805,"branch":{"file":"application/models/v2/Offer.php","line":836}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/offer/upload/files Datei zu einer Ressource hochladen Lädt eine Datei hoch und ordnet sie der über meta angegebenen kontoeigenen Ressource zu. Multipart-Teil file enthält die Bytes; meta ist ein JSON-String {data:{resource_type,resource_id,filetype,...}}. resource_id muss exakt die zum resource_type passende öffentliche offer-, user- oder admin-ID sein. Für Angebotsdateien: internalOfferFile (geschützt) oder externalOfferFile (öffentlich), jeweils bis 20 MiB. Weitere Typen: avatar (8 MiB, JPEG/PNG/WebP), sign und image2/image3 (2 MiB, JPEG/PNG/GIF/WebP). image1 ist im Getter vorhanden, wird vom Upload-Hook aber abgelehnt. Avatare und Signaturen ersetzen bestehende gleichartige Einträge. Dateityp-, Eigentümer- und Kontoprüfungen gelten vor dem Schreiben. - Operation-ID: post_v2_offer_upload_files - Dokumentation: /docs/api/v2/operations/post-v2-offer-upload-files-197db9c80cfc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/offer/upload/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offer/upload/files"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/offer/upload/files" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_offer_upload_files","summary":"Datei zu einer Ressource hochladen","description":"Lädt eine Datei hoch und ordnet sie der über meta angegebenen kontoeigenen Ressource zu. Multipart-Teil file enthält die Bytes; meta ist ein JSON-String {data:{resource_type,resource_id,filetype,...}}. resource_id muss exakt die zum resource_type passende öffentliche offer-, user- oder admin-ID sein. Für Angebotsdateien: internalOfferFile (geschützt) oder externalOfferFile (öffentlich), jeweils bis 20 MiB. Weitere Typen: avatar (8 MiB, JPEG/PNG/WebP), sign und image2/image3 (2 MiB, JPEG/PNG/GIF/WebP). image1 ist im Getter vorhanden, wird vom Upload-Hook aber abgelehnt. Avatare und Signaturen ersetzen bestehende gleichartige Einträge. Dateityp-, Eigentümer- und Kontoprüfungen gelten vor dem Schreiben.","tags":["offer"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Daten des beschriebenen Ergebnisses im JSON-data-Feld; error und Ergebniskennzeichen beachten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["files_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","meta"],"properties":{"file":{"type":"string","format":"binary","description":"Uploaded file bytes; enforce server MIME and size limits."},"meta":{"type":"string","contentMediaType":"application/json","description":"JSON-Umschlag mit data.resource_type, data.resource_id und data.filetype als Pflichtangaben; filename und sprachcodiertes description sind optional. Die Ressource muss im Konto bestehen. Beispiel synthetisch.","examples":["{\"data\":{\"resource_type\":\"offer\",\"resource_id\":\"offer_012345abcdef_123456\",\"filetype\":\"externalOfferFile\",\"filename\":\"example.pdf\"}}"],"contentSchema":{"type":"object","properties":{"data":{"type":"object","properties":{"resource_type":{"type":"string","enum":["offer","user","admin"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]}]},"filetype":{"type":"string","enum":["avatar","sign","internalOfferFile","externalOfferFile","image2","image3"]},"filename":{"type":"string"},"description":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":true,"required":["resource_type","resource_id","filetype"]}},"additionalProperties":true,"required":["data"]}}},"additionalProperties":false},"examples":{"upload":{"summary":"Illustrative multipart parts; supply actual file bytes.","value":{"file":"","meta":"{\"data\":{\"resource_type\":\"offer\",\"resource_id\":\"offer_012345abcdef_123456\",\"filetype\":\"externalOfferFile\",\"filename\":\"example.pdf\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offer.php","method":"postOffer","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offer","short":"offe","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offerdef Angebotsbezeichnungen auflisten Liest Angebotsbezeichnungen im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert globale sowie kontoeigene Bezeichnungen für Angebote und Termine (singular, plural, date_singular, date_plural). Der language-Header wählt die übersetzte Bezeichnung; es gibt keine schreibenden Offerdef-Operationen. - Operation-ID: get_v2_offerdef - Dokumentation: /docs/api/v2/operations/get-v2-offerdef-14aa12915519/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offerdef' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerdef"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerdef" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offerdef","summary":"Angebotsbezeichnungen auflisten","description":"Liest Angebotsbezeichnungen im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert globale sowie kontoeigene Bezeichnungen für Angebote und Termine (singular, plural, date_singular, date_plural). Der language-Header wählt die übersetzte Bezeichnung; es gibt keine schreibenden Offerdef-Operationen.","tags":["offerdef"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offerdef_012345abcdef_123456":{"id":"offerdef_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerdef.php","method":"getOfferdef","line":121},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offerdef","short":"ofdf","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offerdef/{id} Angebotsbezeichnung abrufen Liest Angebotsbezeichnung anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Liefert globale sowie kontoeigene Bezeichnungen für Angebote und Termine (singular, plural, date_singular, date_plural). Der language-Header wählt die übersetzte Bezeichnung; es gibt keine schreibenden Offerdef-Operationen. - Operation-ID: get_v2_offerdef_id - Dokumentation: /docs/api/v2/operations/get-v2-offerdef-id-9bd526576792/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offerdef/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerdef/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerdef/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offerdef_id","summary":"Angebotsbezeichnung abrufen","description":"Liest Angebotsbezeichnung anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Liefert globale sowie kontoeigene Bezeichnungen für Angebote und Termine (singular, plural, date_singular, date_plural). Der language-Header wählt die übersetzte Bezeichnung; es gibt keine schreibenden Offerdef-Operationen.","tags":["offerdef"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offerdef_012345abcdef_123456":{"id":"offerdef_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerdef.php","method":"getOfferdef","line":121},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offerdef","short":"ofdf","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offerdef/list Angebotsbezeichnungen auflisten Liest Angebotsbezeichnungen im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert globale sowie kontoeigene Bezeichnungen für Angebote und Termine (singular, plural, date_singular, date_plural). Der language-Header wählt die übersetzte Bezeichnung; es gibt keine schreibenden Offerdef-Operationen. - Operation-ID: get_v2_offerdef_list - Dokumentation: /docs/api/v2/operations/get-v2-offerdef-list-9c4269b656bf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offerdef/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerdef/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerdef/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offerdef_list","summary":"Angebotsbezeichnungen auflisten","description":"Liest Angebotsbezeichnungen im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Liefert globale sowie kontoeigene Bezeichnungen für Angebote und Termine (singular, plural, date_singular, date_plural). Der language-Header wählt die übersetzte Bezeichnung; es gibt keine schreibenden Offerdef-Operationen.","tags":["offerdef"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerdef_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offerdef_012345abcdef_123456":{"id":"offerdef_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerdef_012345abcdef_123456"]},"singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":36},"plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39},"date_singular":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":42},"date_plural":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":45}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerdef.php","method":"getOfferdef","line":121},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offerdef","short":"ofdf","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/offerlist Anfrageliste löschen Löscht Anfrageliste im gewählten Konto. Eine öffentliche Ziel-ID ist erforderlich: als id im JSON-Umschlag oder als gleichnamiger Query-Parameter. Ein JSON-id hat Vorrang. Die Anfrageliste wird als gelöscht markiert (iamdelete). - Operation-ID: delete_v2_offerlist - Dokumentation: /docs/api/v2/operations/delete-v2-offerlist-77b0a3c4ad04/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "offerlist_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/offerlist' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "offerlist_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"offerlist_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_offerlist","summary":"Anfrageliste löschen","description":"Löscht Anfrageliste im gewählten Konto. Eine öffentliche Ziel-ID ist erforderlich: als id im JSON-Umschlag oder als gleichnamiger Query-Parameter. Ein JSON-id hat Vorrang. Die Anfrageliste wird als gelöscht markiert (iamdelete).","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offerlist_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Anfrageliste löschen","value":{"id":"offerlist_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"deleteOfferlist","line":244},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offerlist Anfragelisten auflisten Liest Anfragelisten im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Der zusätzliche Filter offer begrenzt auf Listen, die einem Angebot zugeordnet sind. - Operation-ID: get_v2_offerlist - Dokumentation: /docs/api/v2/operations/get-v2-offerlist-90740d61190a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offerlist' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offerlist","summary":"Anfragelisten auflisten","description":"Liest Anfragelisten im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Der zusätzliche Filter offer begrenzt auf Listen, die einem Angebot zugeordnet sind.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offerlist_012345abcdef_123456":{"id":"offerlist_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"getOfferlist","line":258},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/offerlist Anfrageliste anlegen Legt Anfrageliste mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. names ist eine sprachcodierte Namenszuordnung. send_booking_pdf/create_reservations folgen den im Feldschema beschriebenen Abhängigkeiten und der Reihenfolge der Felder. validity_limitation_expiration_rules wird sortiert und numerisch normalisiert. - Operation-ID: post_v2_offerlist - Dokumentation: /docs/api/v2/operations/post-v2-offerlist-4d7c8818d074/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "names": { "en": "Example request list", "de": "Beispielliste" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/offerlist' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "names": { "en": "Example request list", "de": "Beispielliste" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"names\":{\"en\":\"Example request list\",\"de\":\"Beispielliste\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_offerlist","summary":"Anfrageliste anlegen","description":"Legt Anfrageliste mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. names ist eine sprachcodierte Namenszuordnung. send_booking_pdf/create_reservations folgen den im Feldschema beschriebenen Abhängigkeiten und der Reihenfolge der Felder. validity_limitation_expiration_rules wird sortiert und numerisch normalisiert.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offerlist_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"names":{"x-edoobox-recognizedKeys":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"],"description":"Retains recognized language keys. String values aP-normalize/truncate250; nonstrings become empty. Unknown keys ignored; other top-level shapes normalize empty list. No rejection for these normalization cases.\n\nIterate only PHP array; recognized language keys retained; per-value aP(...,true), json_encode. Empty map becomes PHP[] and JSON[]; nonempty keyed map decodes to object.","x-edoobox-canonicalType":"object","x-edoobox-canonicalPropertyValues":"string","x-edoobox-schemaStatus":"source-reviewed","examples":[{"en":"Example request list","de":"Beispielliste"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":61,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"send_booking_pdf":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":70,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"create_reservations":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":80,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"sending_reminder_days":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":90,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"sending_reminder":{"x-edoobox-knownInputs":["before_offer_startdate","from_date_of_entry"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["before_offer_startdate"],"x-edoobox-sourceLine":100},"validity_limitation":{"x-edoobox-knownInputs":["before_offer_startdate","from_date_of_entry","expiration_rules"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["before_offer_startdate"],"x-edoobox-sourceLine":129},"validity_limitation_days":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":164,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":["number","string"],"description":"PHP is_numeric input; numeric strings are accepted and converted with intval. This is not a nonnegative/range check."},"days_until_start":{"type":["number","string"],"description":"PHP is_numeric input; numeric strings are accepted and converted with intval. This is not a nonnegative/range check."}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"description":"Expiration rules are sorted by days_until_start; both numeric inputs are converted with intval. [] clears the rules.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["names"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"names":{"x-edoobox-recognizedKeys":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"],"description":"Retains recognized language keys. String values aP-normalize/truncate250; nonstrings become empty. Unknown keys ignored; other top-level shapes normalize empty list. No rejection for these normalization cases.\n\nIterate only PHP array; recognized language keys retained; per-value aP(...,true), json_encode. Empty map becomes PHP[] and JSON[]; nonempty keyed map decodes to object.","x-edoobox-canonicalType":"object","x-edoobox-canonicalPropertyValues":"string","x-edoobox-schemaStatus":"source-reviewed","examples":[{"en":"Example request list","de":"Beispielliste"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":61,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"send_booking_pdf":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":70,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"create_reservations":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":80,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"sending_reminder_days":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":90,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"sending_reminder":{"x-edoobox-knownInputs":["before_offer_startdate","from_date_of_entry"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["before_offer_startdate"],"x-edoobox-sourceLine":100},"validity_limitation":{"x-edoobox-knownInputs":["before_offer_startdate","from_date_of_entry","expiration_rules"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["before_offer_startdate"],"x-edoobox-sourceLine":129},"validity_limitation_days":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":164,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":["number","string"],"description":"PHP is_numeric input; numeric strings are accepted and converted with intval. This is not a nonnegative/range check."},"days_until_start":{"type":["number","string"],"description":"PHP is_numeric input; numeric strings are accepted and converted with intval. This is not a nonnegative/range check."}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"description":"Expiration rules are sorted by days_until_start; both numeric inputs are converted with intval. [] clears the rules.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["names"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"names":{"en":"Example request list","de":"Beispielliste"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"postOfferlist","line":200},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offerlist Anfrageliste aktualisieren Ändert die übergebenen schreibbaren Felder in data für Anfrageliste. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. names ist eine sprachcodierte Namenszuordnung. send_booking_pdf/create_reservations folgen den im Feldschema beschriebenen Abhängigkeiten und der Reihenfolge der Felder. validity_limitation_expiration_rules wird sortiert und numerisch normalisiert. - Operation-ID: put_v2_offerlist - Dokumentation: /docs/api/v2/operations/put-v2-offerlist-2aadb6b4ef17/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "names": { "en": "Example request list", "de": "Beispielliste" } }, "id": "offerlist_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offerlist' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "names": { "en": "Example request list", "de": "Beispielliste" } }, "id": "offerlist_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"names\":{\"en\":\"Example request list\",\"de\":\"Beispielliste\"}},\"id\":\"offerlist_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offerlist","summary":"Anfrageliste aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Anfrageliste. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. names ist eine sprachcodierte Namenszuordnung. send_booking_pdf/create_reservations folgen den im Feldschema beschriebenen Abhängigkeiten und der Reihenfolge der Felder. validity_limitation_expiration_rules wird sortiert und numerisch normalisiert.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offerlist_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"names":{"x-edoobox-recognizedKeys":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"],"description":"Retains recognized language keys. String values aP-normalize/truncate250; nonstrings become empty. Unknown keys ignored; other top-level shapes normalize empty list. No rejection for these normalization cases.\n\nIterate only PHP array; recognized language keys retained; per-value aP(...,true), json_encode. Empty map becomes PHP[] and JSON[]; nonempty keyed map decodes to object.","x-edoobox-canonicalType":"object","x-edoobox-canonicalPropertyValues":"string","x-edoobox-schemaStatus":"source-reviewed","examples":[{"en":"Example request list","de":"Beispielliste"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"standard_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":61,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"send_booking_pdf":{"x-edoobox-canonicalType":"boolean","description":"Canonical JSON booleans supported; PHP loose truth comparison also coerces other JSON values. No strict wire boolean validator.\n\nLoose boolean -> 0/1. Disabling PDF also disables reservations. Shared func runs once, so only first matching input field drives the hook.\n\nRuntime preconditions / rejection: Missing list -> callback false -> ED90100. Enable reservations requires truthy send_booking_pdf in request or stored PDF enabled, otherwise pdf_send_required.\n\nScope / limitations: Combined two-flag updates are input-order-sensitive: PDF first true updates only PDF; reservations first can read paired PDF=true without persisting that paired PDF value.","x-edoobox-schemaStatus":"source-reviewed","examples":[false],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"create_reservations":{"x-edoobox-canonicalType":"boolean","description":"Canonical JSON booleans supported; PHP loose truth comparison also coerces other JSON values. No strict wire boolean validator.\n\nLoose boolean -> 0/1. Disabling PDF also disables reservations. Shared func runs once, so only first matching input field drives the hook.\n\nRuntime preconditions / rejection: Missing list -> callback false -> ED90100. Enable reservations requires truthy send_booking_pdf in request or stored PDF enabled, otherwise pdf_send_required.\n\nScope / limitations: Combined two-flag updates are input-order-sensitive: PDF first true updates only PDF; reservations first can read paired PDF=true without persisting that paired PDF value.","x-edoobox-schemaStatus":"source-reviewed","examples":[false],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"sending_reminder_days":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":90,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"sending_reminder":{"x-edoobox-knownInputs":["before_offer_startdate","from_date_of_entry"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["before_offer_startdate"],"x-edoobox-sourceLine":100},"validity_limitation":{"x-edoobox-knownInputs":["before_offer_startdate","from_date_of_entry","expiration_rules"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["before_offer_startdate"],"x-edoobox-sourceLine":129},"validity_limitation_days":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":164,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":["number","string"],"description":"PHP is_numeric input; numeric strings are accepted and converted with intval. This is not a nonnegative/range check."},"days_until_start":{"type":["number","string"],"description":"PHP is_numeric input; numeric strings are accepted and converted with intval. This is not a nonnegative/range check."}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"description":"Expiration rules are sorted by days_until_start; both numeric inputs are converted with intval. [] clears the rules.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Anfrageliste aktualisieren","value":{"data":{"names":{"en":"Example request list","de":"Beispielliste"}},"id":"offerlist_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"updateOfferlist","line":217},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/offerlist/{id} Anfrageliste löschen Löscht Anfrageliste im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Die Anfrageliste wird als gelöscht markiert (iamdelete). - Operation-ID: delete_v2_offerlist_id - Dokumentation: /docs/api/v2/operations/delete-v2-offerlist-id-ebbfa9464e9e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "offerlist_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/offerlist/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "offerlist_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"offerlist_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_offerlist_id","summary":"Anfrageliste löschen","description":"Löscht Anfrageliste im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Die Anfrageliste wird als gelöscht markiert (iamdelete).","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offerlist_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Anfrageliste löschen","value":{"id":"offerlist_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"deleteOfferlist","line":244},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offerlist/{id} Anfrageliste abrufen Liest Anfrageliste anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Der zusätzliche Filter offer begrenzt auf Listen, die einem Angebot zugeordnet sind. - Operation-ID: get_v2_offerlist_id - Dokumentation: /docs/api/v2/operations/get-v2-offerlist-id-5a41b9b59a3f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offerlist/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offerlist_id","summary":"Anfrageliste abrufen","description":"Liest Anfrageliste anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Der zusätzliche Filter offer begrenzt auf Listen, die einem Angebot zugeordnet sind.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offerlist_012345abcdef_123456":{"id":"offerlist_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"getOfferlist","line":258},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offerlist/{id} Anfrageliste aktualisieren Ändert die übergebenen schreibbaren Felder in data für Anfrageliste. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. names ist eine sprachcodierte Namenszuordnung. send_booking_pdf/create_reservations folgen den im Feldschema beschriebenen Abhängigkeiten und der Reihenfolge der Felder. validity_limitation_expiration_rules wird sortiert und numerisch normalisiert. - Operation-ID: put_v2_offerlist_id - Dokumentation: /docs/api/v2/operations/put-v2-offerlist-id-66b6f1cdbc1a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "names": { "en": "Example request list", "de": "Beispielliste" } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offerlist/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "names": { "en": "Example request list", "de": "Beispielliste" } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"names\":{\"en\":\"Example request list\",\"de\":\"Beispielliste\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offerlist_id","summary":"Anfrageliste aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Anfrageliste. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. names ist eine sprachcodierte Namenszuordnung. send_booking_pdf/create_reservations folgen den im Feldschema beschriebenen Abhängigkeiten und der Reihenfolge der Felder. validity_limitation_expiration_rules wird sortiert und numerisch normalisiert.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offerlist_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"names":{"x-edoobox-recognizedKeys":["de","en","es","fr","cn","cs","ar","it","nl","pl","sv","sk","tr","hu","pt","br","fi","sl","ja","da","ch","rm","el","ko","ro","th","ms","vi","id"],"description":"Retains recognized language keys. String values aP-normalize/truncate250; nonstrings become empty. Unknown keys ignored; other top-level shapes normalize empty list. No rejection for these normalization cases.\n\nIterate only PHP array; recognized language keys retained; per-value aP(...,true), json_encode. Empty map becomes PHP[] and JSON[]; nonempty keyed map decodes to object.","x-edoobox-canonicalType":"object","x-edoobox-canonicalPropertyValues":"string","x-edoobox-schemaStatus":"source-reviewed","examples":[{"en":"Example request list","de":"Beispielliste"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":61,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"send_booking_pdf":{"x-edoobox-canonicalType":"boolean","description":"Canonical JSON booleans supported; PHP loose truth comparison also coerces other JSON values. No strict wire boolean validator.\n\nLoose boolean -> 0/1. Disabling PDF also disables reservations. Shared func runs once, so only first matching input field drives the hook.\n\nRuntime preconditions / rejection: Missing list -> callback false -> ED90100. Enable reservations requires truthy send_booking_pdf in request or stored PDF enabled, otherwise pdf_send_required.\n\nScope / limitations: Combined two-flag updates are input-order-sensitive: PDF first true updates only PDF; reservations first can read paired PDF=true without persisting that paired PDF value.","x-edoobox-schemaStatus":"source-reviewed","examples":[false],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"x-edoobox-canonicalType":"boolean","description":"Canonical JSON booleans supported; PHP loose truth comparison also coerces other JSON values. No strict wire boolean validator.\n\nLoose boolean -> 0/1. Disabling PDF also disables reservations. Shared func runs once, so only first matching input field drives the hook.\n\nRuntime preconditions / rejection: Missing list -> callback false -> ED90100. Enable reservations requires truthy send_booking_pdf in request or stored PDF enabled, otherwise pdf_send_required.\n\nScope / limitations: Combined two-flag updates are input-order-sensitive: PDF first true updates only PDF; reservations first can read paired PDF=true without persisting that paired PDF value.","x-edoobox-schemaStatus":"source-reviewed","examples":[false],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":90,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"sending_reminder":{"x-edoobox-knownInputs":["before_offer_startdate","from_date_of_entry"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["before_offer_startdate"],"x-edoobox-sourceLine":100},"validity_limitation":{"x-edoobox-knownInputs":["before_offer_startdate","from_date_of_entry","expiration_rules"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["before_offer_startdate"],"x-edoobox-sourceLine":129},"validity_limitation_days":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":164,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":["number","string"],"description":"PHP is_numeric input; numeric strings are accepted and converted with intval. This is not a nonnegative/range check."},"days_until_start":{"type":["number","string"],"description":"PHP is_numeric input; numeric strings are accepted and converted with intval. This is not a nonnegative/range check."}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"description":"Expiration rules are sorted by days_until_start; both numeric inputs are converted with intval. [] clears the rules.","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"names":{"en":"Example request list","de":"Beispielliste"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"updateOfferlist","line":217},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offerlist/{id}/send_reminder Erinnerungen zur Anfrageliste versenden Stellt Erinnerungen für alle offenen Einträge (Status 0/1) dieser Liste und des angegebenen Angebots in den E-Mail-Postausgang. Nur Einträge mit E-Mail-Adresse werden berücksichtigt. Bei Erinnerungen wird zusätzlich sending_reminder_done aktualisiert. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offerlist_id_send_reminder - Dokumentation: /docs/api/v2/operations/put-v2-offerlist-id-send-reminder-f79bd04c1da1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offerlist/'"${PATH_ID}"'/send_reminder' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist/{id}/send_reminder"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist/{id}/send_reminder" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offerlist_id_send_reminder","summary":"Erinnerungen zur Anfrageliste versenden","description":"Stellt Erinnerungen für alle offenen Einträge (Status 0/1) dieser Liste und des angegebenen Angebots in den E-Mail-Postausgang. Nur Einträge mit E-Mail-Adresse werden berücksichtigt. Bei Erinnerungen wird zusätzlich sending_reminder_done aktualisiert. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offerlist":"offerlist_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"offerlist":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},"send":{"type":"boolean"}},"additionalProperties":true,"required":["offerlist","send"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["offer"]},"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","examples":["offerlist_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Erinnerungen zur Anfrageliste versenden","value":{"data":{"offer":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"updateOfferlist","line":217,"branch":{"file":"application/models/v2/Offerlist.php","line":231}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offerlist/{id}/send_request Anfrage-E-Mails versenden Stellt Anfragen für alle offenen Einträge (Status 0/1) dieser Liste und des angegebenen Angebots in den E-Mail-Postausgang. Nur Einträge mit E-Mail-Adresse werden berücksichtigt. Wiederholte Aufrufe können weitere E-Mails erzeugen. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offerlist_id_send_request - Dokumentation: /docs/api/v2/operations/put-v2-offerlist-id-send-request-fdfbf0a7c19b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offerlist/'"${PATH_ID}"'/send_request' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist/{id}/send_request"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist/{id}/send_request" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offerlist_id_send_request","summary":"Anfrage-E-Mails versenden","description":"Stellt Anfragen für alle offenen Einträge (Status 0/1) dieser Liste und des angegebenen Angebots in den E-Mail-Postausgang. Nur Einträge mit E-Mail-Adresse werden berücksichtigt. Wiederholte Aufrufe können weitere E-Mails erzeugen. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offerlist":"offerlist_012345abcdef_123456","send":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"offerlist":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},"send":{"type":"boolean"}},"additionalProperties":true,"required":["offerlist","send"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true,"required":["offer"]},"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","examples":["offerlist_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Anfrage-E-Mails versenden","value":{"data":{"offer":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"updateOfferlist","line":217,"branch":{"file":"application/models/v2/Offerlist.php","line":228}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/offerlist/{id}/user Teilnehmer in einer Anfrageliste verwalten Fügt einen Teilnehmer zur Angebotsliste hinzu, entfernt ihn oder beantwortet einen offenen Eintrag mit accepted/rejected. Das Angebot muss diese Liste bereits verwenden. Je nach Listenkonfiguration und Aktion werden Reservierungen oder Buchungen erzeugt, abgeglichen bzw. entfernt. add kann bereits vorhandene Buchungen berücksichtigen; dafür handle_duplicate_user_bookings wählen. allow_existing_offerlist_user wirkt nur bei exakt JSON true. Mit update ist list_status fachlich sinnvoll; ohne Wert kann der Legacy-Handler ein unverändertes Ergebnis bestätigen. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_offerlist_id_user - Dokumentation: /docs/api/v2/operations/put-v2-offerlist-id-user-40f4f2ce2337/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "user": "user_012345abcdef_123456", "change": "add", "handle_duplicate_user_bookings": "ignore" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/offerlist/'"${PATH_ID}"'/user' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist/{id}/user"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "user": "user_012345abcdef_123456", "change": "add", "handle_duplicate_user_bookings": "ignore" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist/{id}/user" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"user\":\"user_012345abcdef_123456\",\"change\":\"add\",\"handle_duplicate_user_bookings\":\"ignore\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_offerlist_id_user","summary":"Teilnehmer in einer Anfrageliste verwalten","description":"Fügt einen Teilnehmer zur Angebotsliste hinzu, entfernt ihn oder beantwortet einen offenen Eintrag mit accepted/rejected. Das Angebot muss diese Liste bereits verwenden. Je nach Listenkonfiguration und Aktion werden Reservierungen oder Buchungen erzeugt, abgeglichen bzw. entfernt. add kann bereits vorhandene Buchungen berücksichtigen; dafür handle_duplicate_user_bookings wählen. allow_existing_offerlist_user wirkt nur bei exakt JSON true. Mit update ist list_status fachlich sinnvoll; ohne Wert kann der Legacy-Handler ein unverändertes Ergebnis bestätigen. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"offerlist_012345abcdef_123456","update":true,"booking_id":false,"insert":true,"repaired":false,"mail_required":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerlist_012345abcdef_123456"]},"update":{"type":"boolean"},"booking_id":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"insert":{"type":"boolean"},"repaired":{"type":"boolean"},"mail_required":{"type":"boolean"}},"additionalProperties":true,"required":["id","update","booking_id","insert","repaired","mail_required"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["user_012345abcdef_123456"]},"change":{"type":"string","enum":["add","remove","update"]},"pricecategory":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["pricecategory_012345abcdef_123456"]},"list_status":{"type":"string","enum":["accepted","rejected"]},"handle_duplicate_user_bookings":{"type":"string","enum":["ignore","add_additional_booking"],"default":"ignore"},"allow_existing_offerlist_user":{"description":"Nur exakt true aktiviert die Wiederverwendung bestehender Einträge.","default":false,"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true,"required":["offer","user","change"]},"id":{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","examples":["offerlist_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Teilnehmer in einer Anfrageliste verwalten","value":{"data":{"offer":"offer_012345abcdef_123456","user":"user_012345abcdef_123456","change":"add","handle_duplicate_user_bookings":"ignore"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"updateOfferlist","line":217,"branch":{"file":"application/models/v2/Offerlist.php","line":225}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/offerlist/list Anfragelisten auflisten Liest Anfragelisten im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Der zusätzliche Filter offer begrenzt auf Listen, die einem Angebot zugeordnet sind. - Operation-ID: get_v2_offerlist_list - Dokumentation: /docs/api/v2/operations/get-v2-offerlist-list-1fe43cbe537e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/offerlist/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/offerlist/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/offerlist/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_offerlist_list","summary":"Anfragelisten auflisten","description":"Liest Anfragelisten im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Der zusätzliche Filter offer begrenzt auf Listen, die einem Angebot zugeordnet sind.","tags":["offerlist"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"offerlist_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"offerlist_012345abcdef_123456":{"id":"offerlist_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offerlist_012345abcdef_123456"]},"names":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":40,"lineStart":40,"lineEnd":60,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"7956957a412f60f6e3070294cadf1bd97e5ef9da265681325534d92ab8d6f63b","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Per-name normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language map comes from config."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Configured language keys."},{"file":"application/models/v2/Offerlistfunc.php","line":13,"lineStart":13,"lineEnd":29,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"26c531659adaea6f55c86ef22a93e9edcb2f2278639b295ddfd408f13feb8770","reason":"Before-insert validates PDF/reservation condition, no names restriction."},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"standard_list":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":61,"type":"boolean"},"send_booking_pdf":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":70,"lineStart":70,"lineEnd":79,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"1ccc96bf979446a948cf2f7d8200a0ae16b02ce2c4f43c680d86d63a4119f661","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"create_reservations":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Offerlist.php","line":80,"lineStart":80,"lineEnd":89,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"db2e7f13cb9784ee53fd7843f37122e626a058270e03c911263cb65506ed195a","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Offerlistfunc.php","line":31,"lineStart":31,"lineEnd":92,"sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813","spanSha256":"7cfb8735180e7c7196158123bb85469d68dd713f47a3bd3e8aa386450c9a025c","reason":"func delegates to _changeReservationPDF"},{"file":"application/models/v2/Offerlist.php","line":200,"lineStart":200,"lineEnd":234,"sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e","spanSha256":"f3488b0401acc7d95d2f622ef6640006b48aaaaacf82db8d51c2c42f6e8e214a","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"sending_reminder_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":90,"type":"integer"},"sending_reminder":{"enum":["before_offer_startdate","from_date_of_entry","no_reminder"]},"validity_limitation":{"enum":["before_offer_startdate","from_date_of_entry","expiration_rules","always_valid"]},"validity_limitation_days":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":164,"type":"integer"},"validity_limitation_expiration_rules":{"type":"array","items":{"type":"object","properties":{"validity_period_days":{"type":"integer"},"days_until_start":{"type":"integer"}},"additionalProperties":true,"required":["validity_period_days","days_until_start"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Offerlistfunc.php","line":24,"reason":"POST before-insert delegates the supplied rules to the update normalizer.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlistfunc.php","line":2731,"reason":"PUT requires array records with two numeric values and serializes converted integer rules.","sha256":"4748ae007d6847d720efa83664080941246b1176307935c653fbce0115a8f813"},{"file":"application/models/v2/Offerlist.php","line":179,"reason":"Response json_decodes stored normalized rules.","sha256":"62913e07896532286a9bd753caaf1a07d9917f68fdab4ab9842013fb4d02969e"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Offerlist.php","method":"getOfferlist","line":258},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"offerlist","short":"ofli","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/outbox Nachrichten im Postausgang auflisten Liest erstellte und versandte Nachrichten mit Empfänger, Betreff, HTML, Erstellungs-/Versandzeit und Fehlerzähler. Der Abruf versendet keine Nachricht. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_outbox - Dokumentation: /docs/api/v2/operations/get-v2-outbox-0b62caa3d049/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/outbox' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/outbox"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/outbox" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_outbox","summary":"Nachrichten im Postausgang auflisten","description":"Liest erstellte und versandte Nachrichten mit Empfänger, Betreff, HTML, Erstellungs-/Versandzeit und Fehlerzähler. Der Abruf versendet keine Nachricht. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["outbox"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"outbox_012345abcdef_123456":{"id":"outbox_012345abcdef_123456","to":"person@example.org","to_name":"Beispiel Person","subject":"Kursinformation","html":"

Herzlich willkommen.

","date_send":false,"error":0}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Outbox.php","method":"getOutbox","line":218},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"outbox","short":"outb","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/outbox/{id} Nachricht im postausgang lesen Liest erstellte und versandte Nachrichten mit Empfänger, Betreff, HTML, Erstellungs-/Versandzeit und Fehlerzähler. Der Abruf versendet keine Nachricht. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_outbox_id - Dokumentation: /docs/api/v2/operations/get-v2-outbox-id-8280facd1e4e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/outbox/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/outbox/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/outbox/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_outbox_id","summary":"Nachricht im postausgang lesen","description":"Liest erstellte und versandte Nachrichten mit Empfänger, Betreff, HTML, Erstellungs-/Versandzeit und Fehlerzähler. Der Abruf versendet keine Nachricht. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["outbox"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"outbox_012345abcdef_123456":{"id":"outbox_012345abcdef_123456","to":"person@example.org","to_name":"Beispiel Person","subject":"Kursinformation","html":"

Herzlich willkommen.

","date_send":false,"error":0}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Outbox.php","method":"getOutbox","line":218},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"outbox","short":"outb","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/outbox/{id}/view Gespeicherte E-Mail als HTML anzeigen Zeigt die gespeicherte Nachricht aus dem Postausgang mit Versandinformationen als direkte HTML-Seite. Es wird keine E-Mail verschickt. Die öffentliche outbox_-ID im Pfad wird in den erforderlichen ID-Filter übernommen. - Operation-ID: get_v2_outbox_id_view - Dokumentation: /docs/api/v2/operations/get-v2-outbox-id-view-e1bf38e80591/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/outbox/'"${PATH_ID}"'/view' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/outbox/{id}/view"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/outbox/{id}/view" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_outbox_id_view","summary":"Gespeicherte E-Mail als HTML anzeigen","description":"Zeigt die gespeicherte Nachricht aus dem Postausgang mit Versandinformationen als direkte HTML-Seite. Es wird keine E-Mail verschickt. Die öffentliche outbox_-ID im Pfad wird in den erforderlichen ID-Filter übernommen.","tags":["outbox"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"Direkte HTML-Vorschau; ungültiger ID-Filter wird als JSON-Fehler zurückgegeben.","content":{"text/html":{"schema":{"type":"string"},"example":"

Kursinformation

"},"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"}},"additionalProperties":true},"example":{"error":"ED80100","success":false}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Outbox.php","line":228,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Outbox.php","line":228},"reason":"Reads and renders an existing mail; does not send it."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Outbox.php","method":"getOutbox","line":218,"branch":{"file":"application/models/v2/Outbox.php","line":228}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"outbox","short":"outb","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/outbox/list Nachrichten im Postausgang auflisten Liest erstellte und versandte Nachrichten mit Empfänger, Betreff, HTML, Erstellungs-/Versandzeit und Fehlerzähler. Der Abruf versendet keine Nachricht. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_outbox_list - Dokumentation: /docs/api/v2/operations/get-v2-outbox-list-38ccfbc1fd12/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/outbox/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/outbox/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/outbox/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_outbox_list","summary":"Nachrichten im Postausgang auflisten","description":"Liest erstellte und versandte Nachrichten mit Empfänger, Betreff, HTML, Erstellungs-/Versandzeit und Fehlerzähler. Der Abruf versendet keine Nachricht. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["outbox"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"outbox_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"outbox_012345abcdef_123456":{"id":"outbox_012345abcdef_123456","to":"person@example.org","to_name":"Beispiel Person","subject":"Kursinformation","html":"

Herzlich willkommen.

","date_send":false,"error":0}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["outbox_012345abcdef_123456"]},"to":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":33,"type":"string"},"to_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":43},"user":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"const":""}],"x-edoobox-contractSource":[{"file":"application/models/v2/Outbox.php","line":78,"reason":"No recipient or the legacy --- marker returns empty string; otherwise response is a public user ID.","sha256":"c70892f52a127af8bdac39d3dad20bb5ac7581c2c2623ec4d3e0061cb695e3a9"}]},"subject":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":86},"html":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":106},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":126},"date_send":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":136},"error":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":146,"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Outbox.php","method":"getOutbox","line":218},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"outbox","short":"outb","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/outbox/list/view Gespeicherte E-Mail als HTML anzeigen Zeigt die gespeicherte Nachricht aus dem Postausgang mit Versandinformationen als direkte HTML-Seite. Es wird keine E-Mail verschickt. Ein filter-Eintrag property=id mit öffentlicher outbox_-ID ist Pflicht. - Operation-ID: get_v2_outbox_list_view - Dokumentation: /docs/api/v2/operations/get-v2-outbox-list-view-a2000ba417be/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | JSON-Filter mit genau der gewünschten öffentlichen Nachricht-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"JSON-Filter mit genau der gewünschten öffentlichen Nachricht-ID.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^outbox_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"outbox_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/outbox/list/view' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/outbox/list/view"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/outbox/list/view" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_outbox_list_view","summary":"Gespeicherte E-Mail als HTML anzeigen","description":"Zeigt die gespeicherte Nachricht aus dem Postausgang mit Versandinformationen als direkte HTML-Seite. Es wird keine E-Mail verschickt. Ein filter-Eintrag property=id mit öffentlicher outbox_-ID ist Pflicht.","tags":["outbox"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"JSON-Filter mit genau der gewünschten öffentlichen Nachricht-ID.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"const":"id"},"value":{"type":"string","pattern":"^outbox_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"id","value":"outbox_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Direkte HTML-Vorschau; ungültiger ID-Filter wird als JSON-Fehler zurückgegeben.","content":{"text/html":{"schema":{"type":"string"},"example":"

Kursinformation

"},"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"success":{"type":"boolean"}},"additionalProperties":true},"example":{"error":"ED80100","success":false}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Outbox.php","line":228},"reason":"Reads and renders an existing mail; does not send it."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Outbox.php","method":"getOutbox","line":218,"branch":{"file":"application/models/v2/Outbox.php","line":228}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"outbox","short":"outb","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/paymentsystem Zahlungs- und Rechnungseinstellungen anzeigen Liest die kontobezogenen Zahlungsanbieter, Rechnungsparameter, Bankangaben und Verbindungskennungen. id ist die von dieser Ressource ausgegebene öffentliche account-ID. Einzelne Flags erscheinen nur bei vorhandenem Datenbankschema. Zugangsfelder sind sensibel und werden durch die bestehenden Projektionen teilweise maskiert; die bestehenden Schutzmarkierungen gelten weiterhin. Allgemeine Feldselektion und Listenfilter sind möglich. - Operation-ID: get_v2_paymentsystem - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-9e94fb12f51a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem","summary":"Zahlungs- und Rechnungseinstellungen anzeigen","description":"Liest die kontobezogenen Zahlungsanbieter, Rechnungsparameter, Bankangaben und Verbindungskennungen. id ist die von dieser Ressource ausgegebene öffentliche account-ID. Einzelne Flags erscheinen nur bei vorhandenem Datenbankschema. Zugangsfelder sind sensibel und werden durch die bestehenden Projektionen teilweise maskiert; die bestehenden Schutzmarkierungen gelten weiterhin. Allgemeine Feldselektion und Listenfilter sind möglich.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste der Zahlungs-/Rechnungseinstellungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"account_012345abcdef_123456":{"id":"account_012345abcdef_123456","paypal_v2":false,"invoice_auto":true,"invoice_manual":true,"invoice_holder":"Beispielfirma","invoice_iban":"EXAMPLE-IBAN","invoice_bic":"EXAMPLE-BIC","invoice_paytime":30,"stripe":false,"stripe_connect":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"paypal_v2":{"type":"boolean"},"invoice_auto":{"type":"boolean"},"invoice_manual":{"type":"boolean"},"invoice_paytime_auto":{"type":"boolean"},"invoice_recalculate_due_date":{"type":"boolean"},"invoice_qriso20022":{"type":"boolean"},"invoice_zugferd":{"type":"boolean"},"invoice_show_payment":{"type":"boolean"},"bitpay":{"type":"boolean"},"klarnasofort":{"type":"boolean"},"saferpay":{"type":"boolean"},"authorize":{"type":"boolean"},"stripe":{"type":"boolean"},"stripev3":{"type":"boolean"},"stripe_cc":{"type":"boolean"},"stripe_sepa":{"type":"boolean"},"stripe_sepa_without_validation_paid":{"type":"boolean"},"auto_apply_positive_balance":{"type":"boolean"},"invoice_holder":{"type":"string"},"invoice_iban":{"type":"string"},"invoice_bic":{"type":"string"},"invoice_alias":{"type":"string"},"invoice_zugferd_profiles":{"type":"string"},"invoice_zugferd_managing_director":{"type":"string"},"invoice_zugferd_commercial_register":{"type":"string"},"invoice_zugferd_seller_tax_number":{"type":"string"},"bitpay_key":{"type":"string"},"klarnasofort_key":{"type":"string"},"authorize_login_id":{"type":"string"},"authorize_transaction_key":{"type":"string"},"authorize_client_key":{"type":"string"},"saferpay_version":{"type":"string"},"paypalv2_connect":{"anyOf":[{"type":"string"},{"const":false}]},"stripe_connect":{"anyOf":[{"type":"string"},{"const":false}]},"invoice_number":{"type":"integer"},"invoice_paytime":{"type":"integer"},"invoice_address_email":{"type":"integer"},"saferpay_api":{"type":"array","items":{"type":"string"}},"invoice_qriso20022_config":{"type":"object","properties":{"type":{"type":"string"},"config":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"},"lock":{"type":"boolean"},"value":{"type":["integer","null"]},"field":{"type":["string","null"]},"label":{}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"paypal_v2":{"type":"boolean"},"invoice_auto":{"type":"boolean"},"invoice_manual":{"type":"boolean"},"invoice_paytime_auto":{"type":"boolean"},"invoice_recalculate_due_date":{"type":"boolean"},"invoice_qriso20022":{"type":"boolean"},"invoice_zugferd":{"type":"boolean"},"invoice_show_payment":{"type":"boolean"},"bitpay":{"type":"boolean"},"klarnasofort":{"type":"boolean"},"saferpay":{"type":"boolean"},"authorize":{"type":"boolean"},"stripe":{"type":"boolean"},"stripev3":{"type":"boolean"},"stripe_cc":{"type":"boolean"},"stripe_sepa":{"type":"boolean"},"stripe_sepa_without_validation_paid":{"type":"boolean"},"auto_apply_positive_balance":{"type":"boolean"},"invoice_holder":{"type":"string"},"invoice_iban":{"type":"string"},"invoice_bic":{"type":"string"},"invoice_alias":{"type":"string"},"invoice_zugferd_profiles":{"type":"string"},"invoice_zugferd_managing_director":{"type":"string"},"invoice_zugferd_commercial_register":{"type":"string"},"invoice_zugferd_seller_tax_number":{"type":"string"},"bitpay_key":{"type":"string"},"klarnasofort_key":{"type":"string"},"authorize_login_id":{"type":"string"},"authorize_transaction_key":{"type":"string"},"authorize_client_key":{"type":"string"},"saferpay_version":{"type":"string"},"paypalv2_connect":{"anyOf":[{"type":"string"},{"const":false}]},"stripe_connect":{"anyOf":[{"type":"string"},{"const":false}]},"invoice_number":{"type":"integer"},"invoice_paytime":{"type":"integer"},"invoice_address_email":{"type":"integer"},"saferpay_api":{"type":"array","items":{"type":"string"}},"invoice_qriso20022_config":{"type":"object","properties":{"type":{"type":"string"},"config":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"},"lock":{"type":"boolean"},"value":{"type":["integer","null"]},"field":{"type":["string","null"]},"label":{}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/paymentsystem Zahlungs- und Rechnungseinstellungen speichern Ändert die übergebenen Felder der eigenen Zahlungskonfiguration. Rechnungsaktivierung benötigt vorhandene Angaben für Inhaber, IBAN und BIC; PayPal/Stripe-Aktivierung setzt eine nutzbare Verbindung voraus. stripev3 lässt sich nach Aktivierung nicht deaktivieren. Saferpay erlaubt partielle Slot-Änderungen und bewahrt maskierte Werte. QR-Konfigurationen und E-Rechnungsprofile werden feldspezifisch validiert. Die früheren PayPal-v1-Felder paypal/paypal_email/paypal_aktiv/paypal_ipn_email werden ignoriert. Beim Basisendpunkt ist die äußere account-ID erforderlich. - Operation-ID: put_v2_paymentsystem - Dokumentation: /docs/api/v2/operations/put-v2-paymentsystem-b4bb32fb9cfa/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "invoice_paytime": 30, "invoice_show_payment": true }, "id": "account_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "invoice_paytime": 30, "invoice_show_payment": true }, "id": "account_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"invoice_paytime\":30,\"invoice_show_payment\":true},\"id\":\"account_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_paymentsystem","summary":"Zahlungs- und Rechnungseinstellungen speichern","description":"Ändert die übergebenen Felder der eigenen Zahlungskonfiguration. Rechnungsaktivierung benötigt vorhandene Angaben für Inhaber, IBAN und BIC; PayPal/Stripe-Aktivierung setzt eine nutzbare Verbindung voraus. stripev3 lässt sich nach Aktivierung nicht deaktivieren. Saferpay erlaubt partielle Slot-Änderungen und bewahrt maskierte Werte. QR-Konfigurationen und E-Rechnungsprofile werden feldspezifisch validiert. Die früheren PayPal-v1-Felder paypal/paypal_email/paypal_aktiv/paypal_ipn_email werden ignoriert. Beim Basisendpunkt ist die äußere account-ID erforderlich.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche account-ID und update; keine Zugangsdaten im Schreibresultat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"paypal_v2":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing PayPal connection must be completed and non-revoked. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":34,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":847,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/paymentsystem/Paypalprovider.php","line":340,"reason":"Connection lookup is account-scoped and completed/non-revoked.","sha256":"b2b234c67075a768b6cb86bd4f0ac591b010433b6a1aadf305afc2e5592cf4b5"},{"file":"application/models/paymentsystem/Paypalprovider.php","line":386,"reason":"Returns false when no matching connection exists.","sha256":"b2b234c67075a768b6cb86bd4f0ac591b010433b6a1aadf305afc2e5592cf4b5"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"paypalv2_connect":false,"invoice_auto":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing invoice_holder, invoice_iban and invoice_bic must be nonempty. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":81,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":437,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Prerequisites are loaded through existing ordinary GET output, not merged from request values.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":585,"reason":"Common storage helper invokes the incoming getter with insert=true.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_manual":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing invoice_holder, invoice_iban and invoice_bic must be nonempty. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":91,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":457,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Prerequisites are loaded through existing ordinary GET output, not merged from request values.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":585,"reason":"Common storage helper invokes the incoming getter with insert=true.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_holder":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":101},"invoice_iban":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":110},"invoice_bic":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":119},"invoice_alias":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":5,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":128},"invoice_number":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":137,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"invoice_paytime":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":147,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"invoice_paytime_auto":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":156,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"invoice_recalculate_due_date":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":165,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"invoice_address_email":{"description":"PHP loose switch recognizes 2/3, including numeric strings; every other input becomes 1. Recognized values are guidance, not a rejecting enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"integer","x-edoobox-recognizedValues":[1,2,3],"examples":[1,2,3],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":174,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1808,"reason":"Joined update invokes getter directly.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_qriso20022":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":207,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"invoice_qriso20022_config":{"description":"Canonical shape in x-edoobox-supportedWireSchema. Type is QRR iff loosely equal to QRR, else SCOR. Rebuilds parts, forces first accepted block 1..6/locked and start-27 block end 27/locked, ignores supplied lock and unknown keys, sanitizes/truncates field to 50, preserves label. Throws for length<1; contiguous noninitial nonnull value longer than range; contiguous end>27 or end===27 with start!=27. Positive-length noncontiguous parts are omitted. No mandatory coverage/minItems/field enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"object","x-edoobox-supportedWireSchema":{"type":"object","properties":{"type":{"type":"string"},"config":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"},"value":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}]},"field":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{},"lock":{}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true},"examples":[{"type":"QRR","config":{"parts":[{"start":1,"end":6,"value":null,"field":null,"label":"Synthetic label"},{"start":7,"end":26,"value":null,"field":"invoice_number","label":null},{"start":27,"end":27,"value":null,"field":null,"label":null}]}}],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":226,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1808,"reason":"Joined update invokes incoming getter directly.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_zugferd":{"description":"Hook validates/defaults profile then calls this getter. A supplied truthy profile must validate. Missing/falsy supplied profile uses existing stored profile; empty storage gets default only on truthy enable, otherwise can reject even disable.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":392,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":500,"reason":"Actual checkZUGFeRDProfile hook and conditional profile rejection/defaulting.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11523,"reason":"edisset treats null and false-like supplied profiles as absent.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":9,"reason":"Locally added profiles and legacy aliases.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":30,"reason":"Validity checks defined constants plus PROFILEDEF.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":106,"reason":"Profile aliases normalize to default name.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_zugferd_profiles":{"type":"string","description":"Single profile-name string validated by the native EdZUGFeRD profile validator and normalized for recognized legacy aliases. An unknown profile returns strict false; the final generic joined writer rejects ED80100 before persistence even when this field is supplied alone. The listed recognized values document known producer names; no universal future library enum is inferred.","x-edoobox-schemaStatus":"source-reviewed-runtime-validation","x-edoobox-recognizedValues":["PROFILE_BASIC","PROFILE_BASICWL","PROFILE_EN16931","PROFILE_EXTENDED","PROFILE_XRECHNUNG","PROFILE_XRECHNUNG_2","PROFILE_XRECHNUNG_2_1","PROFILE_XRECHNUNG_2_2","PROFILE_MINIMUM","PROFILE_XRECHNUNG_2_3","PROFILE_XRECHNUNG_3","PROFILE_XRECHNUNG_2_4","PROFILE_ZUGFERD_2_4","PROFILE_ZUGFERD_2_5"],"examples":["PROFILE_EN16931"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":412,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1808,"reason":"Final joined update rejects strict-false getter results before queueing and persistence.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystemfunc.php","line":500,"reason":"invoice_zugferd hook explicitly validates/defaults profile.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":9,"reason":"Locally added profiles and legacy aliases.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":30,"reason":"Validity checks defined constants plus PROFILEDEF.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":106,"reason":"Profile aliases normalize to default name.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"vendor/horstoeko/zugferd/src/ZugferdProfiles.php","line":37,"reason":"Read-only installed parent constant definitions; no dependency operation.","sha256":"d855d091ef5fa74b135d42a76b1bb8098f0480e2b45bbdfbebf8063db9a7de84"},{"file":"vendor/horstoeko/zugferd/src/ZugferdProfiles.php","line":98,"reason":"Installed parent PROFILEDEF keys.","sha256":"d855d091ef5fa74b135d42a76b1bb8098f0480e2b45bbdfbebf8063db9a7de84"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_zugferd_managing_director":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":434},"invoice_zugferd_commercial_register":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":453},"invoice_zugferd_seller_tax_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":472},"invoice_show_payment":{"description":"Hook delegates to UpdateAccountData, which calls the incoming getter. Loose PHP equality to true stores 1/0; boolean is canonical.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":491,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":477,"reason":"Hook forwards raw value to common helper.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common helper calls field incoming getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"Getter supplies generated 1/0 string to aP.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"bitpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":511,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"bitpay_key":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":520},"klarnasofort":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":530,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"klarnasofort_key":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":540},"saferpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":549,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"saferpay_version":{"description":"Trims string-cast input; exact 1.50 stays 1.50 and every other value becomes 1.23. No rejecting enum or length rule.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-recognizedValues":["1.23","1.50"],"examples":["1.50","1.23"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":558,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1808,"reason":"Joined update directly executes getter.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"aP gets an already normalized version string.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"saferpay_api":{"description":"PUT accepts a partial array or object with positional string fields 0–4. Only supplied, changed credential slots are encoded in the existing legacy storage format so that the provider's single decoding step receives the exact submitted credential bytes. Omitted fields, unchanged decoded values and matching GET masks or all-asterisk placeholders retain their stored bytes. Explicit empty strings clear supplied fields; the Vue reset clears fields 0–3 while retaining the legacy fifth field. Reads use only the authorized tenant. Invalid indices, nonstring values, NUL bytes and the ]=[ delimiter are rejected with ED80100. The hook returns an internal persistence patch; the public PUT response contains only the ordinary id/update acknowledgement.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"array","x-edoobox-sensitive":true,"x-edoobox-supportedWireSchema":{"anyOf":[{"type":"array","maxItems":5,"items":{"type":"string","not":{"anyOf":[{"pattern":"\\]=\\["},{"pattern":"\\u0000"}]}}},{"type":"object","propertyNames":{"enum":["0","1","2","3","4"]},"additionalProperties":{"type":"string","not":{"anyOf":[{"pattern":"\\]=\\["},{"pattern":"\\u0000"}]}}}]},"examples":[{"3":"SYNTHETIC_PASSWORD"}],"anyOf":[{"type":"array","maxItems":5,"items":{"type":"string","not":{"anyOf":[{"pattern":"\\]=\\["},{"pattern":"\\u0000"}]}}},{"type":"object","propertyNames":{"enum":["0","1","2","3","4"]},"additionalProperties":{"type":"string","not":{"anyOf":[{"pattern":"\\]=\\["},{"pattern":"\\u0000"}]}}}],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":585,"reason":"Exact Structure declaration and response projection: each stored slot is decoded once; password slot 3 always masks nonempty values, including short passwords. Other slots retain their established masking pattern.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1867,"reason":"Dispatches the original payload to the hook, merges its internal persistence patch, and returns only the normal id/update acknowledgement after a successful transaction.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":882,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":27,"reason":"The PUT hook validates positional string patches, reads only authorized-tenant storage, and encodes only changed slots for the existing provider decode. Omitted fields, unchanged decoded values and known mask placeholders retain stored bytes. Returns an internal persistence patch only.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Per-slot secret masking.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Earlier independent field-level review; its legacy saferpay_api merge description is superseded by the hook and regression-test source references in this contract.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"},{"file":"tests/V2/WebMcpSaferpayConfigurationTest.php","line":1,"reason":"Pest executes the real configuration hook, GET projection and controlled Provider Initialize/Assert: byte preservation, quotes/backslashes through actual HTTP Basic authentication, short-password masking, placeholder/clear semantics, tenant binding and invalid-shape rejection. All DB/HTTP boundaries use in-memory doubles.","sha256":"c871916d1cc812ff91313d819ae953cd5436c0b1344688c1d1257921e3c443cb"}]},"authorize":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing authorize_login_id, authorize_transaction_key and authorize_client_key must be nonempty. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":617,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":8,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Prerequisites are loaded through existing ordinary GET output, not merged from request values.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common storage helper invokes the incoming getter with insert=true.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"authorize_login_id":{"description":"Sensitive credential: strings are trimmed, line breaks replaced, SQL-escaped and truncated to 250 characters by aP; nonstrings normalize to empty. These are normalization rules, not rejecting maxLength/type rules.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-sensitive":true,"examples":["SYNTHETIC_ONLY_VALUE"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":637,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":84,"reason":"Actual hook forwards original field value.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Storage helper invokes getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"aP string normalization and nonstring empty fallback.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Actual response masking.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"authorize_transaction_key":{"description":"Sensitive credential: strings are trimmed, line breaks replaced, SQL-escaped and truncated to 250 characters by aP; nonstrings normalize to empty. These are normalization rules, not rejecting maxLength/type rules.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-sensitive":true,"examples":["SYNTHETIC_ONLY_VALUE"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":657,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":90,"reason":"Actual hook forwards original field value.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Storage helper invokes getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"aP string normalization and nonstring empty fallback.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Actual response masking.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"authorize_client_key":{"description":"Sensitive credential: strings are trimmed, line breaks replaced, SQL-escaped and truncated to 250 characters by aP; nonstrings normalize to empty. These are normalization rules, not rejecting maxLength/type rules.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-sensitive":true,"examples":["SYNTHETIC_ONLY_VALUE"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":677,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":96,"reason":"Actual hook forwards original field value.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Storage helper invokes getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"aP string normalization and nonstring empty fallback.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Actual response masking.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripe":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing stripe_connect must not be false, then existing stripev3 must be strictly true. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":697,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":102,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Prerequisites are loaded through existing ordinary GET output, not merged from request values.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common storage helper invokes the incoming getter with insert=true.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripev3":{"description":"One-way V3 acceptance. If existing stripev3 is false, any submitted value activates it and inserts the default fee row. When already accepted, a loose-false input throws stripev3_cannot_be_deactivated.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":717,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":127,"reason":"Actual one-way activation hook; no getter/type gate before first activation.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Current value comes from existing ordinary GET output.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Writes through incoming Structure getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripe_cc":{"description":"Hook delegates to UpdateAccountData, which calls the incoming getter. Loose PHP equality to true stores 1/0; boolean is canonical.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":737,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":157,"reason":"Hook forwards raw value to common helper.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common helper calls field incoming getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"Getter supplies generated 1/0 string to aP.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripe_sepa":{"description":"Hook delegates to UpdateAccountData, which calls the incoming getter. Loose PHP equality to true stores 1/0; boolean is canonical.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":757,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":163,"reason":"Hook forwards raw value to common helper.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common helper calls field incoming getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"Getter supplies generated 1/0 string to aP.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripe_connect":false,"stripe_sepa_without_validation_paid":{"description":"Nur bei vorhandener zugehöriger Datenbankspalte verfügbar; PHP-Wahrheitswert-Normalisierung.","x-edoobox-canonicalType":"boolean","examples":[true,false]},"auto_apply_positive_balance":{"description":"Nur bei vorhandener zugehöriger Datenbankspalte verfügbar; PHP-Wahrheitswert-Normalisierung.","x-edoobox-canonicalType":"boolean","examples":[true,false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["paypalv2_connect","stripe_connect"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Zahlungs- und Rechnungseinstellungen speichern","value":{"data":{"invoice_paytime":30,"invoice_show_payment":true},"id":"account_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"updatePaymentsystem","line":886},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/paymentsystem/{id} Zahlungs- und Rechnungseinstellungen anzeigen Liest die kontobezogenen Zahlungsanbieter, Rechnungsparameter, Bankangaben und Verbindungskennungen. id ist die von dieser Ressource ausgegebene öffentliche account-ID. Einzelne Flags erscheinen nur bei vorhandenem Datenbankschema. Zugangsfelder sind sensibel und werden durch die bestehenden Projektionen teilweise maskiert; die bestehenden Schutzmarkierungen gelten weiterhin. Allgemeine Feldselektion und Listenfilter sind möglich. - Operation-ID: get_v2_paymentsystem_id - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-id-b0d4628beddf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | account-ID, wie von paymentsystem ausgegeben. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"account-ID, wie von paymentsystem ausgegeben.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_id","summary":"Zahlungs- und Rechnungseinstellungen anzeigen","description":"Liest die kontobezogenen Zahlungsanbieter, Rechnungsparameter, Bankangaben und Verbindungskennungen. id ist die von dieser Ressource ausgegebene öffentliche account-ID. Einzelne Flags erscheinen nur bei vorhandenem Datenbankschema. Zugangsfelder sind sensibel und werden durch die bestehenden Projektionen teilweise maskiert; die bestehenden Schutzmarkierungen gelten weiterhin. Allgemeine Feldselektion und Listenfilter sind möglich.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"account-ID, wie von paymentsystem ausgegeben.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste der Zahlungs-/Rechnungseinstellungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"account_012345abcdef_123456":{"id":"account_012345abcdef_123456","paypal_v2":false,"invoice_auto":true,"invoice_manual":true,"invoice_holder":"Beispielfirma","invoice_iban":"EXAMPLE-IBAN","invoice_bic":"EXAMPLE-BIC","invoice_paytime":30,"stripe":false,"stripe_connect":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"paypal_v2":{"type":"boolean"},"invoice_auto":{"type":"boolean"},"invoice_manual":{"type":"boolean"},"invoice_paytime_auto":{"type":"boolean"},"invoice_recalculate_due_date":{"type":"boolean"},"invoice_qriso20022":{"type":"boolean"},"invoice_zugferd":{"type":"boolean"},"invoice_show_payment":{"type":"boolean"},"bitpay":{"type":"boolean"},"klarnasofort":{"type":"boolean"},"saferpay":{"type":"boolean"},"authorize":{"type":"boolean"},"stripe":{"type":"boolean"},"stripev3":{"type":"boolean"},"stripe_cc":{"type":"boolean"},"stripe_sepa":{"type":"boolean"},"stripe_sepa_without_validation_paid":{"type":"boolean"},"auto_apply_positive_balance":{"type":"boolean"},"invoice_holder":{"type":"string"},"invoice_iban":{"type":"string"},"invoice_bic":{"type":"string"},"invoice_alias":{"type":"string"},"invoice_zugferd_profiles":{"type":"string"},"invoice_zugferd_managing_director":{"type":"string"},"invoice_zugferd_commercial_register":{"type":"string"},"invoice_zugferd_seller_tax_number":{"type":"string"},"bitpay_key":{"type":"string"},"klarnasofort_key":{"type":"string"},"authorize_login_id":{"type":"string"},"authorize_transaction_key":{"type":"string"},"authorize_client_key":{"type":"string"},"saferpay_version":{"type":"string"},"paypalv2_connect":{"anyOf":[{"type":"string"},{"const":false}]},"stripe_connect":{"anyOf":[{"type":"string"},{"const":false}]},"invoice_number":{"type":"integer"},"invoice_paytime":{"type":"integer"},"invoice_address_email":{"type":"integer"},"saferpay_api":{"type":"array","items":{"type":"string"}},"invoice_qriso20022_config":{"type":"object","properties":{"type":{"type":"string"},"config":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"},"lock":{"type":"boolean"},"value":{"type":["integer","null"]},"field":{"type":["string","null"]},"label":{}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"paypal_v2":{"type":"boolean"},"invoice_auto":{"type":"boolean"},"invoice_manual":{"type":"boolean"},"invoice_paytime_auto":{"type":"boolean"},"invoice_recalculate_due_date":{"type":"boolean"},"invoice_qriso20022":{"type":"boolean"},"invoice_zugferd":{"type":"boolean"},"invoice_show_payment":{"type":"boolean"},"bitpay":{"type":"boolean"},"klarnasofort":{"type":"boolean"},"saferpay":{"type":"boolean"},"authorize":{"type":"boolean"},"stripe":{"type":"boolean"},"stripev3":{"type":"boolean"},"stripe_cc":{"type":"boolean"},"stripe_sepa":{"type":"boolean"},"stripe_sepa_without_validation_paid":{"type":"boolean"},"auto_apply_positive_balance":{"type":"boolean"},"invoice_holder":{"type":"string"},"invoice_iban":{"type":"string"},"invoice_bic":{"type":"string"},"invoice_alias":{"type":"string"},"invoice_zugferd_profiles":{"type":"string"},"invoice_zugferd_managing_director":{"type":"string"},"invoice_zugferd_commercial_register":{"type":"string"},"invoice_zugferd_seller_tax_number":{"type":"string"},"bitpay_key":{"type":"string"},"klarnasofort_key":{"type":"string"},"authorize_login_id":{"type":"string"},"authorize_transaction_key":{"type":"string"},"authorize_client_key":{"type":"string"},"saferpay_version":{"type":"string"},"paypalv2_connect":{"anyOf":[{"type":"string"},{"const":false}]},"stripe_connect":{"anyOf":[{"type":"string"},{"const":false}]},"invoice_number":{"type":"integer"},"invoice_paytime":{"type":"integer"},"invoice_address_email":{"type":"integer"},"saferpay_api":{"type":"array","items":{"type":"string"}},"invoice_qriso20022_config":{"type":"object","properties":{"type":{"type":"string"},"config":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"},"lock":{"type":"boolean"},"value":{"type":["integer","null"]},"field":{"type":["string","null"]},"label":{}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/paymentsystem/{id} Zahlungs- und Rechnungseinstellungen speichern Ändert die übergebenen Felder der eigenen Zahlungskonfiguration. Rechnungsaktivierung benötigt vorhandene Angaben für Inhaber, IBAN und BIC; PayPal/Stripe-Aktivierung setzt eine nutzbare Verbindung voraus. stripev3 lässt sich nach Aktivierung nicht deaktivieren. Saferpay erlaubt partielle Slot-Änderungen und bewahrt maskierte Werte. QR-Konfigurationen und E-Rechnungsprofile werden feldspezifisch validiert. Die früheren PayPal-v1-Felder paypal/paypal_email/paypal_aktiv/paypal_ipn_email werden ignoriert. Beim Basisendpunkt ist die äußere account-ID erforderlich. - Operation-ID: put_v2_paymentsystem_id - Dokumentation: /docs/api/v2/operations/put-v2-paymentsystem-id-0887ec8eb7e5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | account-ID aus paymentsystem. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"account-ID aus paymentsystem.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "invoice_paytime": 30, "invoice_show_payment": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "invoice_paytime": 30, "invoice_show_payment": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"invoice_paytime\":30,\"invoice_show_payment\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_paymentsystem_id","summary":"Zahlungs- und Rechnungseinstellungen speichern","description":"Ändert die übergebenen Felder der eigenen Zahlungskonfiguration. Rechnungsaktivierung benötigt vorhandene Angaben für Inhaber, IBAN und BIC; PayPal/Stripe-Aktivierung setzt eine nutzbare Verbindung voraus. stripev3 lässt sich nach Aktivierung nicht deaktivieren. Saferpay erlaubt partielle Slot-Änderungen und bewahrt maskierte Werte. QR-Konfigurationen und E-Rechnungsprofile werden feldspezifisch validiert. Die früheren PayPal-v1-Felder paypal/paypal_email/paypal_aktiv/paypal_ipn_email werden ignoriert. Beim Basisendpunkt ist die äußere account-ID erforderlich.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"account-ID aus paymentsystem.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche account-ID und update; keine Zugangsdaten im Schreibresultat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"paypal_v2":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing PayPal connection must be completed and non-revoked. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":34,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":847,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/paymentsystem/Paypalprovider.php","line":340,"reason":"Connection lookup is account-scoped and completed/non-revoked.","sha256":"b2b234c67075a768b6cb86bd4f0ac591b010433b6a1aadf305afc2e5592cf4b5"},{"file":"application/models/paymentsystem/Paypalprovider.php","line":386,"reason":"Returns false when no matching connection exists.","sha256":"b2b234c67075a768b6cb86bd4f0ac591b010433b6a1aadf305afc2e5592cf4b5"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"paypalv2_connect":false,"invoice_auto":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing invoice_holder, invoice_iban and invoice_bic must be nonempty. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":81,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":437,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Prerequisites are loaded through existing ordinary GET output, not merged from request values.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":585,"reason":"Common storage helper invokes the incoming getter with insert=true.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_manual":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing invoice_holder, invoice_iban and invoice_bic must be nonempty. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":91,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":457,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Prerequisites are loaded through existing ordinary GET output, not merged from request values.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":585,"reason":"Common storage helper invokes the incoming getter with insert=true.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_holder":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":101},"invoice_iban":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":110},"invoice_bic":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":119},"invoice_alias":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":5,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":128},"invoice_number":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":137,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"invoice_paytime":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":147,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"invoice_paytime_auto":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":156,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"invoice_recalculate_due_date":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":165,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"invoice_address_email":{"description":"PHP loose switch recognizes 2/3, including numeric strings; every other input becomes 1. Recognized values are guidance, not a rejecting enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"integer","x-edoobox-recognizedValues":[1,2,3],"examples":[1,2,3],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":174,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1808,"reason":"Joined update invokes getter directly.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_qriso20022":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":207,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"invoice_qriso20022_config":{"description":"Canonical shape in x-edoobox-supportedWireSchema. Type is QRR iff loosely equal to QRR, else SCOR. Rebuilds parts, forces first accepted block 1..6/locked and start-27 block end 27/locked, ignores supplied lock and unknown keys, sanitizes/truncates field to 50, preserves label. Throws for length<1; contiguous noninitial nonnull value longer than range; contiguous end>27 or end===27 with start!=27. Positive-length noncontiguous parts are omitted. No mandatory coverage/minItems/field enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"object","x-edoobox-supportedWireSchema":{"type":"object","properties":{"type":{"type":"string"},"config":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"},"value":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}]},"field":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{},"lock":{}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true},"examples":[{"type":"QRR","config":{"parts":[{"start":1,"end":6,"value":null,"field":null,"label":"Synthetic label"},{"start":7,"end":26,"value":null,"field":"invoice_number","label":null},{"start":27,"end":27,"value":null,"field":null,"label":null}]}}],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":226,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1808,"reason":"Joined update invokes incoming getter directly.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_zugferd":{"description":"Hook validates/defaults profile then calls this getter. A supplied truthy profile must validate. Missing/falsy supplied profile uses existing stored profile; empty storage gets default only on truthy enable, otherwise can reject even disable.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":392,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":500,"reason":"Actual checkZUGFeRDProfile hook and conditional profile rejection/defaulting.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11523,"reason":"edisset treats null and false-like supplied profiles as absent.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":9,"reason":"Locally added profiles and legacy aliases.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":30,"reason":"Validity checks defined constants plus PROFILEDEF.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":106,"reason":"Profile aliases normalize to default name.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_zugferd_profiles":{"type":"string","description":"Single profile-name string validated by the native EdZUGFeRD profile validator and normalized for recognized legacy aliases. An unknown profile returns strict false; the final generic joined writer rejects ED80100 before persistence even when this field is supplied alone. The listed recognized values document known producer names; no universal future library enum is inferred.","x-edoobox-schemaStatus":"source-reviewed-runtime-validation","x-edoobox-recognizedValues":["PROFILE_BASIC","PROFILE_BASICWL","PROFILE_EN16931","PROFILE_EXTENDED","PROFILE_XRECHNUNG","PROFILE_XRECHNUNG_2","PROFILE_XRECHNUNG_2_1","PROFILE_XRECHNUNG_2_2","PROFILE_MINIMUM","PROFILE_XRECHNUNG_2_3","PROFILE_XRECHNUNG_3","PROFILE_XRECHNUNG_2_4","PROFILE_ZUGFERD_2_4","PROFILE_ZUGFERD_2_5"],"examples":["PROFILE_EN16931"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":412,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1808,"reason":"Final joined update rejects strict-false getter results before queueing and persistence.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystemfunc.php","line":500,"reason":"invoice_zugferd hook explicitly validates/defaults profile.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":9,"reason":"Locally added profiles and legacy aliases.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":30,"reason":"Validity checks defined constants plus PROFILEDEF.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"application/libraries/edoobox/EdZUGFeRD.php","line":106,"reason":"Profile aliases normalize to default name.","sha256":"ccd96e8ffbacf35638370441d41a61fa02240c51e58db3bc07402746aec7870d"},{"file":"vendor/horstoeko/zugferd/src/ZugferdProfiles.php","line":37,"reason":"Read-only installed parent constant definitions; no dependency operation.","sha256":"d855d091ef5fa74b135d42a76b1bb8098f0480e2b45bbdfbebf8063db9a7de84"},{"file":"vendor/horstoeko/zugferd/src/ZugferdProfiles.php","line":98,"reason":"Installed parent PROFILEDEF keys.","sha256":"d855d091ef5fa74b135d42a76b1bb8098f0480e2b45bbdfbebf8063db9a7de84"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"invoice_zugferd_managing_director":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":434},"invoice_zugferd_commercial_register":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":453},"invoice_zugferd_seller_tax_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":80,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":472},"invoice_show_payment":{"description":"Hook delegates to UpdateAccountData, which calls the incoming getter. Loose PHP equality to true stores 1/0; boolean is canonical.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":491,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":477,"reason":"Hook forwards raw value to common helper.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common helper calls field incoming getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"Getter supplies generated 1/0 string to aP.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"bitpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":511,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"bitpay_key":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":520},"klarnasofort":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":530,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"klarnasofort_key":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":540},"saferpay":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":549,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"saferpay_version":{"description":"Trims string-cast input; exact 1.50 stays 1.50 and every other value becomes 1.23. No rejecting enum or length rule.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-recognizedValues":["1.23","1.50"],"examples":["1.50","1.23"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":558,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT dispatches API_DATA with Modal Paymentsystem; named deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1808,"reason":"Joined update directly executes getter.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"aP gets an already normalized version string.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"saferpay_api":{"description":"PUT accepts a partial array or object with positional string fields 0–4. Only supplied, changed credential slots are encoded in the existing legacy storage format so that the provider's single decoding step receives the exact submitted credential bytes. Omitted fields, unchanged decoded values and matching GET masks or all-asterisk placeholders retain their stored bytes. Explicit empty strings clear supplied fields; the Vue reset clears fields 0–3 while retaining the legacy fifth field. Reads use only the authorized tenant. Invalid indices, nonstring values, NUL bytes and the ]=[ delimiter are rejected with ED80100. The hook returns an internal persistence patch; the public PUT response contains only the ordinary id/update acknowledgement.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"array","x-edoobox-sensitive":true,"x-edoobox-supportedWireSchema":{"anyOf":[{"type":"array","maxItems":5,"items":{"type":"string","not":{"anyOf":[{"pattern":"\\]=\\["},{"pattern":"\\u0000"}]}}},{"type":"object","propertyNames":{"enum":["0","1","2","3","4"]},"additionalProperties":{"type":"string","not":{"anyOf":[{"pattern":"\\]=\\["},{"pattern":"\\u0000"}]}}}]},"examples":[{"3":"SYNTHETIC_PASSWORD"}],"anyOf":[{"type":"array","maxItems":5,"items":{"type":"string","not":{"anyOf":[{"pattern":"\\]=\\["},{"pattern":"\\u0000"}]}}},{"type":"object","propertyNames":{"enum":["0","1","2","3","4"]},"additionalProperties":{"type":"string","not":{"anyOf":[{"pattern":"\\]=\\["},{"pattern":"\\u0000"}]}}}],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":585,"reason":"Exact Structure declaration and response projection: each stored slot is decoded once; password slot 3 always masks nonempty values, including short passwords. Other slots retain their established masking pattern.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1867,"reason":"Dispatches the original payload to the hook, merges its internal persistence patch, and returns only the normal id/update acknowledgement after a successful transaction.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":882,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":27,"reason":"The PUT hook validates positional string patches, reads only authorized-tenant storage, and encodes only changed slots for the existing provider decode. Omitted fields, unchanged decoded values and known mask placeholders retain stored bytes. Returns an internal persistence patch only.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Per-slot secret masking.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Earlier independent field-level review; its legacy saferpay_api merge description is superseded by the hook and regression-test source references in this contract.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"},{"file":"tests/V2/WebMcpSaferpayConfigurationTest.php","line":1,"reason":"Pest executes the real configuration hook, GET projection and controlled Provider Initialize/Assert: byte preservation, quotes/backslashes through actual HTTP Basic authentication, short-password masking, placeholder/clear semantics, tenant binding and invalid-shape rejection. All DB/HTTP boundaries use in-memory doubles.","sha256":"c871916d1cc812ff91313d819ae953cd5436c0b1344688c1d1257921e3c443cb"}]},"authorize":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing authorize_login_id, authorize_transaction_key and authorize_client_key must be nonempty. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":617,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":8,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Prerequisites are loaded through existing ordinary GET output, not merged from request values.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common storage helper invokes the incoming getter with insert=true.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"authorize_login_id":{"description":"Sensitive credential: strings are trimmed, line breaks replaced, SQL-escaped and truncated to 250 characters by aP; nonstrings normalize to empty. These are normalization rules, not rejecting maxLength/type rules.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-sensitive":true,"examples":["SYNTHETIC_ONLY_VALUE"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":637,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":84,"reason":"Actual hook forwards original field value.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Storage helper invokes getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"aP string normalization and nonstring empty fallback.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Actual response masking.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"authorize_transaction_key":{"description":"Sensitive credential: strings are trimmed, line breaks replaced, SQL-escaped and truncated to 250 characters by aP; nonstrings normalize to empty. These are normalization rules, not rejecting maxLength/type rules.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-sensitive":true,"examples":["SYNTHETIC_ONLY_VALUE"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":657,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":90,"reason":"Actual hook forwards original field value.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Storage helper invokes getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"aP string normalization and nonstring empty fallback.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Actual response masking.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"authorize_client_key":{"description":"Sensitive credential: strings are trimmed, line breaks replaced, SQL-escaped and truncated to 250 characters by aP; nonstrings normalize to empty. These are normalization rules, not rejecting maxLength/type rules.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"string","x-edoobox-sensitive":true,"examples":["SYNTHETIC_ONLY_VALUE"],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":677,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":96,"reason":"Actual hook forwards original field value.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Storage helper invokes getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"aP string normalization and nonstring empty fallback.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Actual response masking.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripe":{"description":"Canonical boolean; PHP loose truthiness chooses enable/disable. Existing stripe_connect must not be false, then existing stripev3 must be strictly true. No boolean-only wire-type rejection is implemented.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":697,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":102,"reason":"Actual activation hook and prerequisite conditions.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystem.php","line":857,"reason":"Ordinary POST payment settings creation is unsupported.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Prerequisites are loaded through existing ordinary GET output, not merged from request values.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common storage helper invokes the incoming getter with insert=true.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripev3":{"description":"One-way V3 acceptance. If existing stripev3 is false, any submitted value activates it and inserts the default fee row. When already accepted, a loose-false input throws stripev3_cannot_be_deactivated.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":717,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":127,"reason":"Actual one-way activation hook; no getter/type gate before first activation.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":413,"reason":"Current value comes from existing ordinary GET output.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Writes through incoming Structure getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripe_cc":{"description":"Hook delegates to UpdateAccountData, which calls the incoming getter. Loose PHP equality to true stores 1/0; boolean is canonical.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":737,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":157,"reason":"Hook forwards raw value to common helper.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common helper calls field incoming getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"Getter supplies generated 1/0 string to aP.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripe_sepa":{"description":"Hook delegates to UpdateAccountData, which calls the incoming getter. Loose PHP equality to true stores 1/0; boolean is canonical.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Paymentsystem.php","line":757,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact func hook with original full payload: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Paymentsystem.php","line":886,"reason":"Default PUT sanitizes only unrelated legacy PayPal fields then dispatches API_DATA with Modal Paymentsystem; deauthorize actions dispatch elsewhere.","sha256":"cb08046675a1b90b34d42c1dc7326f55adf9b09914ac9d2caa0c30d995aca864"},{"file":"application/models/v2/Paymentsystemfunc.php","line":163,"reason":"Hook forwards raw value to common helper.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/models/v2/Paymentsystemfunc.php","line":570,"reason":"Common helper calls field incoming getter.","sha256":"3318c0cf960ede00b4948f80dcdf8191523e0eaa703fe7883e411e6ae4fdee10"},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"reason":"Getter supplies generated 1/0 string to aP.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"stripe_connect":false,"stripe_sepa_without_validation_paid":{"description":"Nur bei vorhandener zugehöriger Datenbankspalte verfügbar; PHP-Wahrheitswert-Normalisierung.","x-edoobox-canonicalType":"boolean","examples":[true,false]},"auto_apply_positive_balance":{"description":"Nur bei vorhandener zugehöriger Datenbankspalte verfügbar; PHP-Wahrheitswert-Normalisierung.","x-edoobox-canonicalType":"boolean","examples":[true,false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["paypalv2_connect","stripe_connect"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zahlungs- und Rechnungseinstellungen speichern","value":{"data":{"invoice_paytime":30,"invoice_show_payment":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"updatePaymentsystem","line":886},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/paymentsystem/{id}/paypal_deauthorize PayPal-Verbindung trennen Markiert die aktuelle PayPal-Verbindung lokal als widerrufen und deaktiviert PayPal v2. Das Ergebnisfeld stripe_user_id enthält hier aus historischen Gründen die PayPal-Merchant-Kennung. Ohne bestehende Verbindung ist update=false. Der Server verwendet das angemeldete Konto; die Pfad-ID wird als id zurückgegeben. - Operation-ID: put_v2_paymentsystem_id_paypal_deauthorize - Dokumentation: /docs/api/v2/operations/put-v2-paymentsystem-id-paypal-deauthorize-005c3ba6dcf4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | account-ID aus paymentsystem; das betroffene Konto kommt aus der Anmeldung. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"account-ID aus paymentsystem; das betroffene Konto kommt aus der Anmeldung.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/'"${PATH_ID}"'/paypal_deauthorize' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/{id}/paypal_deauthorize"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/{id}/paypal_deauthorize" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_paymentsystem_id_paypal_deauthorize","summary":"PayPal-Verbindung trennen","description":"Markiert die aktuelle PayPal-Verbindung lokal als widerrufen und deaktiviert PayPal v2. Das Ergebnisfeld stripe_user_id enthält hier aus historischen Gründen die PayPal-Merchant-Kennung. Ohne bestehende Verbindung ist update=false. Der Server verwendet das angemeldete Konto; die Pfad-ID wird als id zurückgegeben.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"account-ID aus paymentsystem; das betroffene Konto kommt aus der Anmeldung.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"id/update und bei vorhandener Verbindung stripe_user_id (bei PayPal: Merchant-Kennung).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"stripe_user_id":"MERCHANT-EXAMPLE"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"update":{"type":"boolean"},"stripe_user_id":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"PayPal-Verbindung trennen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"updatePaymentsystem","line":886,"branch":{"file":"application/models/v2/Paymentsystem.php","line":898}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/paymentsystem/{id}/stripe_deauthorize Stripe-Verbindung trennen Widerruft die Stripe-Partnerfreigabe, entfernt lokal die Verbindung und deaktiviert Stripe. Auch wenn der Anbieterwiderruf fehlschlägt, wird lokal getrennt und error_code=no_connection_to_this_partner_account_exist kann bei error=false erscheinen. Ohne bestehende Verbindung ist update=false. Der Server verwendet das angemeldete Konto; die Pfad-ID wird als id zurückgegeben. - Operation-ID: put_v2_paymentsystem_id_stripe_deauthorize - Dokumentation: /docs/api/v2/operations/put-v2-paymentsystem-id-stripe-deauthorize-7b755562e364/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | account-ID aus paymentsystem; das betroffene Konto kommt aus der Anmeldung. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"account-ID aus paymentsystem; das betroffene Konto kommt aus der Anmeldung.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/'"${PATH_ID}"'/stripe_deauthorize' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/{id}/stripe_deauthorize"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/{id}/stripe_deauthorize" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_paymentsystem_id_stripe_deauthorize","summary":"Stripe-Verbindung trennen","description":"Widerruft die Stripe-Partnerfreigabe, entfernt lokal die Verbindung und deaktiviert Stripe. Auch wenn der Anbieterwiderruf fehlschlägt, wird lokal getrennt und error_code=no_connection_to_this_partner_account_exist kann bei error=false erscheinen. Ohne bestehende Verbindung ist update=false. Der Server verwendet das angemeldete Konto; die Pfad-ID wird als id zurückgegeben.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"account-ID aus paymentsystem; das betroffene Konto kommt aus der Anmeldung.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"id/update und bei vorhandener Verbindung stripe_user_id (bei PayPal: Merchant-Kennung).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"stripe_user_id":"acct_example"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"update":{"type":"boolean"},"stripe_user_id":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Stripe-Verbindung trennen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"updatePaymentsystem","line":886,"branch":{"file":"application/models/v2/Paymentsystem.php","line":895}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/paymentsystem/connect/paypal PayPal-Händlerverbindung vorbereiten Erstellt beim Zahlungsanbieter einen Partner-Referral für das aktuelle Konto und liefert den Browserlink zur Händlerfreigabe (kein edoobox-Konto-Signup). Erfordert Zahlungsanbieter-Schreibrechte. Legt bei fehlender Konfiguration die Standard-PayPal-Gebühr an. system unterstützt ausschließlich EXPRESS_CHECKOUT; paymentmethods wird gelesen, ändert derzeit aber die angeforderten Produkte nicht. - Operation-ID: get_v2_paymentsystem_connect_paypal - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-connect-paypal-ffcba79a6240/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | system | query | nein | Angefordertes PayPal-Produkt. | | paymentmethods | query | nein | Derzeit ohne Änderung der Produkte; optional. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"system","in":"query","required":false,"schema":{"type":"string","enum":["EXPRESS_CHECKOUT"],"default":"EXPRESS_CHECKOUT"},"description":"Angefordertes PayPal-Produkt.","example":"EXPRESS_CHECKOUT"},{"name":"paymentmethods","in":"query","required":false,"schema":{"type":"string"},"description":"Derzeit ohne Änderung der Produkte; optional.","example":"false"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/connect/paypal' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/connect/paypal"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/connect/paypal" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_connect_paypal","summary":"PayPal-Händlerverbindung vorbereiten","description":"Erstellt beim Zahlungsanbieter einen Partner-Referral für das aktuelle Konto und liefert den Browserlink zur Händlerfreigabe (kein edoobox-Konto-Signup). Erfordert Zahlungsanbieter-Schreibrechte. Legt bei fehlender Konfiguration die Standard-PayPal-Gebühr an. system unterstützt ausschließlich EXPRESS_CHECKOUT; paymentmethods wird gelesen, ändert derzeit aber die angeforderten Produkte nicht.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"system","in":"query","required":false,"schema":{"type":"string","enum":["EXPRESS_CHECKOUT"],"default":"EXPRESS_CHECKOUT"},"description":"Angefordertes PayPal-Produkt.","example":"EXPRESS_CHECKOUT"},{"name":"paymentmethods","in":"query","required":false,"schema":{"type":"string"},"description":"Derzeit ohne Änderung der Produkte; optional.","example":"false"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"url zum Anbieter und valid_until (fünf Minuten); kein Redirect dieser Route.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"url":"https://www.paypal.com/bizsignup/partner/entry?token=synthetic","valid_until":"2026-10-15T10:05:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string"},"valid_until":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Paymentsystem.php","line":970,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Paymentsystem.php","line":978},"reason":"Creates a PayPal partner referral and may seed its default fee; native action authorization rejects read-only and read-level actors."},"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/14","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-native-action-authorization":{"short":"pyst","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931,"branch":{"file":"application/models/v2/Paymentsystem.php","line":982}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[3,4],"description":"Native provider-connect action requires current capped paymentsystem level 3/4 and a non-read-only login before creating a referral or OAuth state.","dispatcherLevels":[1,3,4],"effectiveLevels":[3,4]}} ``` --- # GET /v2/paymentsystem/connect/stripe Stripe-Verbindung vorbereiten Stellt einen kurzlebigen, einmaligen OAuth-Zustand für das aktuelle Konto und dessen Berechtigungen aus und liefert die Stripe-Autorisierungs-URL. Der GET schreibt den serverseitigen Autorisierungszustand und erfordert Zahlungsanbieter-Schreibrechte. Die eigentliche Browserrückkehr erfolgt über den Auth-OAuth-Callback. - Operation-ID: get_v2_paymentsystem_connect_stripe - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-connect-stripe-dfae925a6e16/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/connect/stripe' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/connect/stripe"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/connect/stripe" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_connect_stripe","summary":"Stripe-Verbindung vorbereiten","description":"Stellt einen kurzlebigen, einmaligen OAuth-Zustand für das aktuelle Konto und dessen Berechtigungen aus und liefert die Stripe-Autorisierungs-URL. Der GET schreibt den serverseitigen Autorisierungszustand und erfordert Zahlungsanbieter-Schreibrechte. Die eigentliche Browserrückkehr erfolgt über den Auth-OAuth-Callback.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"url und valid_until; kein Redirect dieser Route.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"url":"https://connect.stripe.com/oauth/authorize?state=synthetic-state","valid_until":"2026-10-15T10:10:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string"},"valid_until":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Paymentsystem.php","line":951,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"business-mutation","x-edoobox-native-action-authorization":{"short":"pyst","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem"}},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Paymentsystem.php","line":951},"reason":"Persists a browser-bound OAuth authorization state and cookie through OAuthAuthorizationState::issue; the exact native state ACL requires current paymentsystem write authority."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931,"branch":{"file":"application/models/v2/Paymentsystem.php","line":955}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[3,4],"description":"Native provider-connect action requires current capped paymentsystem level 3/4 and a non-read-only login before creating a referral or OAuth state.","dispatcherLevels":[1,3,4],"effectiveLevels":[3,4]}} ``` --- # POST /v2/paymentsystem/connect/stripe_domain Stripe-Zahlungsdomain registrieren Stellt sicher, dass die aktuelle öffentliche edoobox-Domain im verbundenen Stripe-Konto für Wallet-Zahlungen registriert und aktiviert ist. Verwendet die konfigurierte Serverdomain, keine Domain aus dem Request. Ohne Verbindung oder bei lokaler/IP-Domain wird nichts registriert. Anbieterfehler erscheinen als status=error im normalen Resultat. - Operation-ID: post_v2_paymentsystem_connect_stripe_domain - Dokumentation: /docs/api/v2/operations/post-v2-paymentsystem-connect-stripe-domain-b4a483fa851a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/connect/stripe_domain' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/connect/stripe_domain"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/connect/stripe_domain" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_paymentsystem_connect_stripe_domain","summary":"Stripe-Zahlungsdomain registrieren","description":"Stellt sicher, dass die aktuelle öffentliche edoobox-Domain im verbundenen Stripe-Konto für Wallet-Zahlungen registriert und aktiviert ist. Verwendet die konfigurierte Serverdomain, keine Domain aus dem Request. Ohne Verbindung oder bei lokaler/IP-Domain wird nichts registriert. Anbieterfehler erscheinen als status=error im normalen Resultat.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Domain, Registrierungs-/Aktivierungsstatus und Apple-Pay-Status.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"domain_name":"app.edoobox.com","status":"registered","registered":true,"enabled":true,"apple_pay_status":"active"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"domain_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["not_connected","invalid_domain","registered","enabled","created","error"]},"registered":{"type":"boolean"},"enabled":{"type":"boolean"},"apple_pay_status":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Stripe-Zahlungsdomain registrieren","value":{}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Paymentsystem.php","line":858,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"postPaymentsystem","line":857,"branch":{"file":"application/models/v2/Paymentsystem.php","line":862}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/paymentsystem/list Zahlungs- und Rechnungseinstellungen anzeigen Liest die kontobezogenen Zahlungsanbieter, Rechnungsparameter, Bankangaben und Verbindungskennungen. id ist die von dieser Ressource ausgegebene öffentliche account-ID. Einzelne Flags erscheinen nur bei vorhandenem Datenbankschema. Zugangsfelder sind sensibel und werden durch die bestehenden Projektionen teilweise maskiert; die bestehenden Schutzmarkierungen gelten weiterhin. Allgemeine Feldselektion und Listenfilter sind möglich. - Operation-ID: get_v2_paymentsystem_list - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-list-6ebf17e0bf11/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_list","summary":"Zahlungs- und Rechnungseinstellungen anzeigen","description":"Liest die kontobezogenen Zahlungsanbieter, Rechnungsparameter, Bankangaben und Verbindungskennungen. id ist die von dieser Ressource ausgegebene öffentliche account-ID. Einzelne Flags erscheinen nur bei vorhandenem Datenbankschema. Zugangsfelder sind sensibel und werden durch die bestehenden Projektionen teilweise maskiert; die bestehenden Schutzmarkierungen gelten weiterhin. Allgemeine Feldselektion und Listenfilter sind möglich.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"paymentsystem_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste der Zahlungs-/Rechnungseinstellungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"account_012345abcdef_123456":{"id":"account_012345abcdef_123456","paypal_v2":false,"invoice_auto":true,"invoice_manual":true,"invoice_holder":"Beispielfirma","invoice_iban":"EXAMPLE-IBAN","invoice_bic":"EXAMPLE-BIC","invoice_paytime":30,"stripe":false,"stripe_connect":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"paypal_v2":{"type":"boolean"},"invoice_auto":{"type":"boolean"},"invoice_manual":{"type":"boolean"},"invoice_paytime_auto":{"type":"boolean"},"invoice_recalculate_due_date":{"type":"boolean"},"invoice_qriso20022":{"type":"boolean"},"invoice_zugferd":{"type":"boolean"},"invoice_show_payment":{"type":"boolean"},"bitpay":{"type":"boolean"},"klarnasofort":{"type":"boolean"},"saferpay":{"type":"boolean"},"authorize":{"type":"boolean"},"stripe":{"type":"boolean"},"stripev3":{"type":"boolean"},"stripe_cc":{"type":"boolean"},"stripe_sepa":{"type":"boolean"},"stripe_sepa_without_validation_paid":{"type":"boolean"},"auto_apply_positive_balance":{"type":"boolean"},"invoice_holder":{"type":"string"},"invoice_iban":{"type":"string"},"invoice_bic":{"type":"string"},"invoice_alias":{"type":"string"},"invoice_zugferd_profiles":{"type":"string"},"invoice_zugferd_managing_director":{"type":"string"},"invoice_zugferd_commercial_register":{"type":"string"},"invoice_zugferd_seller_tax_number":{"type":"string"},"bitpay_key":{"type":"string"},"klarnasofort_key":{"type":"string"},"authorize_login_id":{"type":"string"},"authorize_transaction_key":{"type":"string"},"authorize_client_key":{"type":"string"},"saferpay_version":{"type":"string"},"paypalv2_connect":{"anyOf":[{"type":"string"},{"const":false}]},"stripe_connect":{"anyOf":[{"type":"string"},{"const":false}]},"invoice_number":{"type":"integer"},"invoice_paytime":{"type":"integer"},"invoice_address_email":{"type":"integer"},"saferpay_api":{"type":"array","items":{"type":"string"}},"invoice_qriso20022_config":{"type":"object","properties":{"type":{"type":"string"},"config":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"},"lock":{"type":"boolean"},"value":{"type":["integer","null"]},"field":{"type":["string","null"]},"label":{}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"paypal_v2":{"type":"boolean"},"invoice_auto":{"type":"boolean"},"invoice_manual":{"type":"boolean"},"invoice_paytime_auto":{"type":"boolean"},"invoice_recalculate_due_date":{"type":"boolean"},"invoice_qriso20022":{"type":"boolean"},"invoice_zugferd":{"type":"boolean"},"invoice_show_payment":{"type":"boolean"},"bitpay":{"type":"boolean"},"klarnasofort":{"type":"boolean"},"saferpay":{"type":"boolean"},"authorize":{"type":"boolean"},"stripe":{"type":"boolean"},"stripev3":{"type":"boolean"},"stripe_cc":{"type":"boolean"},"stripe_sepa":{"type":"boolean"},"stripe_sepa_without_validation_paid":{"type":"boolean"},"auto_apply_positive_balance":{"type":"boolean"},"invoice_holder":{"type":"string"},"invoice_iban":{"type":"string"},"invoice_bic":{"type":"string"},"invoice_alias":{"type":"string"},"invoice_zugferd_profiles":{"type":"string"},"invoice_zugferd_managing_director":{"type":"string"},"invoice_zugferd_commercial_register":{"type":"string"},"invoice_zugferd_seller_tax_number":{"type":"string"},"bitpay_key":{"type":"string"},"klarnasofort_key":{"type":"string"},"authorize_login_id":{"type":"string"},"authorize_transaction_key":{"type":"string"},"authorize_client_key":{"type":"string"},"saferpay_version":{"type":"string"},"paypalv2_connect":{"anyOf":[{"type":"string"},{"const":false}]},"stripe_connect":{"anyOf":[{"type":"string"},{"const":false}]},"invoice_number":{"type":"integer"},"invoice_paytime":{"type":"integer"},"invoice_address_email":{"type":"integer"},"saferpay_api":{"type":"array","items":{"type":"string"}},"invoice_qriso20022_config":{"type":"object","properties":{"type":{"type":"string"},"config":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"},"lock":{"type":"boolean"},"value":{"type":["integer","null"]},"field":{"type":["string","null"]},"label":{}},"additionalProperties":true}}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/paymentsystem/list/fees_export Monatliche Zahlungsgebühren exportieren Lädt eine UTF-8-CSV mit BOM und Semikolon-Trennzeichen für positive edoobox-Gebühren auf verbuchten Stripe- und PayPal-v2-Teilnehmerzahlungen. month ist verpflichtend (YYYY-MM) und darf nicht in der Zukunft liegen. Spalten: Transaktionsnummer beziehungsweise öffentliche pay-ID, System, Datum und Gebührenbetrag mit Währung. Der Server liest Stripe-Gebühren vor der Ausgabe vollständig; Anbieterfehler liefern payment_fees_unavailable statt Teil-CSV. Nur angemeldete Administratoren mit tatsächlichem user_recht>=3. - Operation-ID: get_v2_paymentsystem_list_fees_export - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-list-fees-export-bc72891c2473/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | month | query | ja | Exportmonat in der Serverzeitzone, höchstens aktueller Monat. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"month","in":"query","required":true,"description":"Exportmonat in der Serverzeitzone, höchstens aktueller Monat.","schema":{"type":"string","pattern":"^[1-9][0-9]{3}-(0[1-9]|1[0-2])$"},"example":"2026-10"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_MONTH:?QUERY_MONTH setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/list/fees_export' \ --url-query 'month='"${QUERY_MONTH}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/list/fees_export"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("month", required("QUERY_MONTH")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/list/fees_export" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("month", os.environ["QUERY_MONTH"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_list_fees_export","summary":"Monatliche Zahlungsgebühren exportieren","description":"Lädt eine UTF-8-CSV mit BOM und Semikolon-Trennzeichen für positive edoobox-Gebühren auf verbuchten Stripe- und PayPal-v2-Teilnehmerzahlungen. month ist verpflichtend (YYYY-MM) und darf nicht in der Zukunft liegen. Spalten: Transaktionsnummer beziehungsweise öffentliche pay-ID, System, Datum und Gebührenbetrag mit Währung. Der Server liest Stripe-Gebühren vor der Ausgabe vollständig; Anbieterfehler liefern payment_fees_unavailable statt Teil-CSV. Nur angemeldete Administratoren mit tatsächlichem user_recht>=3.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"month","in":"query","required":true,"description":"Exportmonat in der Serverzeitzone, höchstens aktueller Monat.","schema":{"type":"string","pattern":"^[1-9][0-9]{3}-(0[1-9]|1[0-2])$"},"example":"2026-10"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"CSV-Download mit Content-Disposition und Cache-Control:no-store; JSON bei Fehlern.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true}},"text/csv":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Paymentsystemfunc.php","line":139},"reason":"Reads existing provider debug/fee data or streams CSV. OAuth Stripe authorization is separately classified as a business mutation because it persists browser-bound state."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931,"branch":{"file":"application/models/v2/Paymentsystem.php","line":942}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/paymentsystem/list/fees_months Monate mit Zahlungsgebühren anzeigen Liefert absteigend sortierte Monate mit verbuchten positiven Teilnehmerzahlungen über Stripe oder gebührenpflichtige PayPal-v2-Zahlungen. Verwendet das edoobox-Buchungsdatum und die Serverzeitzone; zukünftige Monate entfallen. Nur angemeldete Administratoren mit tatsächlichem user_recht>=3 dürfen diesen Export lesen. - Operation-ID: get_v2_paymentsystem_list_fees_months - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-list-fees-months-b1d7172230ed/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/list/fees_months' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/list/fees_months"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/list/fees_months" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_list_fees_months","summary":"Monate mit Zahlungsgebühren anzeigen","description":"Liefert absteigend sortierte Monate mit verbuchten positiven Teilnehmerzahlungen über Stripe oder gebührenpflichtige PayPal-v2-Zahlungen. Verwendet das edoobox-Buchungsdatum und die Serverzeitzone; zukünftige Monate entfallen. Nur angemeldete Administratoren mit tatsächlichem user_recht>=3 dürfen diesen Export lesen.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Objekt mit months im Format YYYY-MM.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"months":["2026-10","2026-09"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"months":{"type":"array","items":{"type":"string","pattern":"^[1-9][0-9]{3}-(0[1-9]|1[0-2])$"}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Paymentsystemfunc.php","line":139},"reason":"Reads existing provider debug/fee data or streams CSV. OAuth Stripe authorization is separately classified as a business mutation because it persists browser-bound state."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931,"branch":{"file":"application/models/v2/Paymentsystem.php","line":939}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/paymentsystem/list/paypal PayPal-Händlerverbindung vorbereiten Erstellt beim Zahlungsanbieter einen Partner-Referral für das aktuelle Konto und liefert den Browserlink zur Händlerfreigabe (kein edoobox-Konto-Signup). Erfordert Zahlungsanbieter-Schreibrechte. Legt bei fehlender Konfiguration die Standard-PayPal-Gebühr an. system unterstützt ausschließlich EXPRESS_CHECKOUT; paymentmethods wird gelesen, ändert derzeit aber die angeforderten Produkte nicht. - Operation-ID: get_v2_paymentsystem_list_paypal - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-list-paypal-6309ecdaeb45/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | system | query | nein | Angefordertes PayPal-Produkt. | | paymentmethods | query | nein | Derzeit ohne Änderung der Produkte; optional. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"system","in":"query","required":false,"schema":{"type":"string","enum":["EXPRESS_CHECKOUT"],"default":"EXPRESS_CHECKOUT"},"description":"Angefordertes PayPal-Produkt.","example":"EXPRESS_CHECKOUT"},{"name":"paymentmethods","in":"query","required":false,"schema":{"type":"string"},"description":"Derzeit ohne Änderung der Produkte; optional.","example":"false"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/list/paypal' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/list/paypal"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/list/paypal" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_list_paypal","summary":"PayPal-Händlerverbindung vorbereiten","description":"Erstellt beim Zahlungsanbieter einen Partner-Referral für das aktuelle Konto und liefert den Browserlink zur Händlerfreigabe (kein edoobox-Konto-Signup). Erfordert Zahlungsanbieter-Schreibrechte. Legt bei fehlender Konfiguration die Standard-PayPal-Gebühr an. system unterstützt ausschließlich EXPRESS_CHECKOUT; paymentmethods wird gelesen, ändert derzeit aber die angeforderten Produkte nicht.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"system","in":"query","required":false,"schema":{"type":"string","enum":["EXPRESS_CHECKOUT"],"default":"EXPRESS_CHECKOUT"},"description":"Angefordertes PayPal-Produkt.","example":"EXPRESS_CHECKOUT"},{"name":"paymentmethods","in":"query","required":false,"schema":{"type":"string"},"description":"Derzeit ohne Änderung der Produkte; optional.","example":"false"},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"url zum Anbieter und valid_until (fünf Minuten); kein Redirect dieser Route.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"url":"https://www.paypal.com/bizsignup/partner/entry?token=synthetic","valid_until":"2026-10-15T10:05:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string"},"valid_until":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Paymentsystem.php","line":978},"reason":"Creates a PayPal partner referral and may seed its default fee; native action authorization rejects read-only and read-level actors."},"x-edoobox-get-effect":"business-mutation","x-edoobox-native-action-authorization":{"short":"pyst","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931,"branch":{"file":"application/models/v2/Paymentsystem.php","line":982}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[3,4],"description":"Native provider-connect action requires current capped paymentsystem level 3/4 and a non-read-only login before creating a referral or OAuth state.","dispatcherLevels":[1,3,4],"effectiveLevels":[3,4]}} ``` --- # GET /v2/paymentsystem/list/paypal_status PayPal-Händlerfreigabe aktualisieren Liest den aktuellen Integrationsstatus beim Anbieter und aktualisiert lokal event_COMPLETED sowie bestätigte E-Mail-/Zahlungsflags. Benötigt eine vorhandene nicht widerrufene Händlerverbindung; ohne Verbindung ED80100. status zeigt Zahlungsannahme, email die bestätigte primäre E-Mail. - Operation-ID: get_v2_paymentsystem_list_paypal_status - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-list-paypal-status-72e7f814263b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/list/paypal_status' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/list/paypal_status"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/list/paypal_status" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_list_paypal_status","summary":"PayPal-Händlerfreigabe aktualisieren","description":"Liest den aktuellen Integrationsstatus beim Anbieter und aktualisiert lokal event_COMPLETED sowie bestätigte E-Mail-/Zahlungsflags. Benötigt eine vorhandene nicht widerrufene Händlerverbindung; ohne Verbindung ED80100. status zeigt Zahlungsannahme, email die bestätigte primäre E-Mail.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Objekt mit status und email.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"status":true,"email":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"status":{"type":"boolean"},"email":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Paymentsystem.php","line":992,"reason":"Exact GET paypal_status branch remains separate from guarded paypal connect; delegates only existing-connection status refresh at994. Root approved its bounded readonly maintenance."},"reason":"Refresh PayPal merchant integration state from the provider and persist completed/payment-receivable flags.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/15","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/15","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931,"branch":{"file":"application/models/v2/Paymentsystem.php","line":992}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/paymentsystem/list/stripe Stripe-Verbindung vorbereiten Stellt einen kurzlebigen, einmaligen OAuth-Zustand für das aktuelle Konto und dessen Berechtigungen aus und liefert die Stripe-Autorisierungs-URL. Der GET schreibt den serverseitigen Autorisierungszustand und erfordert Zahlungsanbieter-Schreibrechte. Die eigentliche Browserrückkehr erfolgt über den Auth-OAuth-Callback. - Operation-ID: get_v2_paymentsystem_list_stripe - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-list-stripe-6cd82de845b5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/list/stripe' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/list/stripe"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/list/stripe" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_list_stripe","summary":"Stripe-Verbindung vorbereiten","description":"Stellt einen kurzlebigen, einmaligen OAuth-Zustand für das aktuelle Konto und dessen Berechtigungen aus und liefert die Stripe-Autorisierungs-URL. Der GET schreibt den serverseitigen Autorisierungszustand und erfordert Zahlungsanbieter-Schreibrechte. Die eigentliche Browserrückkehr erfolgt über den Auth-OAuth-Callback.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"url und valid_until; kein Redirect dieser Route.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"url":"https://connect.stripe.com/oauth/authorize?state=synthetic-state","valid_until":"2026-10-15T10:10:00+02:00"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"url":{"type":"string"},"valid_until":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-native-action-authorization":{"short":"pyst","levels":[3,4],"readOnlyAllowed":false,"source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem"}},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Paymentsystem.php","line":951},"reason":"Persists a browser-bound OAuth authorization state and cookie through OAuthAuthorizationState::issue; the exact native state ACL requires current paymentsystem write authority."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931,"branch":{"file":"application/models/v2/Paymentsystem.php","line":955}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[3,4],"description":"Native provider-connect action requires current capped paymentsystem level 3/4 and a non-read-only login before creating a referral or OAuth state.","dispatcherLevels":[1,3,4],"effectiveLevels":[3,4]}} ``` --- # GET /v2/paymentsystem/list/stripe_debug Stripe-Zahlungsbereitschaft diagnostizieren Liest die verbundene Stripe-Kennung, Standard-Zahlungsmethoden-Konfiguration, aktivierte Zahlungsarten und Wallet-Domains direkt beim Anbieter. Gibt bei Teilfehlern weiter die verfügbaren Daten und stabile errors zurück. Erzeugt oder aktiviert selbst keine Zahlungsdomain. - Operation-ID: get_v2_paymentsystem_list_stripe_debug - Dokumentation: /docs/api/v2/operations/get-v2-paymentsystem-list-stripe-debug-e1bcd745fef1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/list/stripe_debug' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/list/stripe_debug"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/list/stripe_debug" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_paymentsystem_list_stripe_debug","summary":"Stripe-Zahlungsbereitschaft diagnostizieren","description":"Liest die verbundene Stripe-Kennung, Standard-Zahlungsmethoden-Konfiguration, aktivierte Zahlungsarten und Wallet-Domains direkt beim Anbieter. Gibt bei Teilfehlern weiter die verfügbaren Daten und stabile errors zurück. Erzeugt oder aktiviert selbst keine Zahlungsdomain.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Diagnoseobjekt; errors kann payment_methods_unavailable/domains_unavailable enthalten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"connected":false,"account_id":null,"livemode":false,"payment_method_configuration":null,"payment_methods":[],"domains":[],"errors":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"connected":{"type":"boolean"},"account_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"livemode":{"type":"boolean"},"payment_method_configuration":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"active":{"type":"boolean"},"is_default":{"type":"boolean"}},"additionalProperties":true},{"type":"null"}]},"payment_methods":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"available":{"type":"boolean"},"enabled":{"type":"boolean"},"preference":{"type":"string"}},"additionalProperties":true}},"domains":{"type":"array","items":{"type":"object","properties":{"domain_name":{"type":"string"},"enabled":{"type":"boolean"},"livemode":{"type":"boolean"},"apple_pay_status":{"type":"string"}},"additionalProperties":true}},"errors":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Paymentsystemfunc.php","line":139},"reason":"Reads existing provider debug/fee data or streams CSV. OAuth Stripe authorization is separately classified as a business mutation because it persists browser-bound state."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"getPaymentsystem","line":931,"branch":{"file":"application/models/v2/Paymentsystem.php","line":975}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/paymentsystem/list/stripe_domain Stripe-Zahlungsdomain registrieren Stellt sicher, dass die aktuelle öffentliche edoobox-Domain im verbundenen Stripe-Konto für Wallet-Zahlungen registriert und aktiviert ist. Verwendet die konfigurierte Serverdomain, keine Domain aus dem Request. Ohne Verbindung oder bei lokaler/IP-Domain wird nichts registriert. Anbieterfehler erscheinen als status=error im normalen Resultat. - Operation-ID: post_v2_paymentsystem_list_stripe_domain - Dokumentation: /docs/api/v2/operations/post-v2-paymentsystem-list-stripe-domain-2b9f0208e9ec/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/paymentsystem/list/stripe_domain' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/paymentsystem/list/stripe_domain"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/paymentsystem/list/stripe_domain" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_paymentsystem_list_stripe_domain","summary":"Stripe-Zahlungsdomain registrieren","description":"Stellt sicher, dass die aktuelle öffentliche edoobox-Domain im verbundenen Stripe-Konto für Wallet-Zahlungen registriert und aktiviert ist. Verwendet die konfigurierte Serverdomain, keine Domain aus dem Request. Ohne Verbindung oder bei lokaler/IP-Domain wird nichts registriert. Anbieterfehler erscheinen als status=error im normalen Resultat.","tags":["paymentsystem"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Domain, Registrierungs-/Aktivierungsstatus und Apple-Pay-Status.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"domain_name":"app.edoobox.com","status":"registered","registered":true,"enabled":true,"apple_pay_status":"active"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"domain_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["not_connected","invalid_domain","registered","enabled","created","error"]},"registered":{"type":"boolean"},"enabled":{"type":"boolean"},"apple_pay_status":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Stripe-Zahlungsdomain registrieren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Paymentsystem.php","method":"postPaymentsystem","line":857,"branch":{"file":"application/models/v2/Paymentsystem.php","line":862}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"paymentsystem","short":"pyst","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/permission Berechtigungsprofil entfernen Entfernt ein eigenes, unbenutztes Berechtigungsprofil. Systemprofile sind gesperrt; bei noch zugeordneten Administratoren oder API-Zugängen bleibt delete=false mit error_code=is_still_in_use. - Operation-ID: delete_v2_permission - Dokumentation: /docs/api/v2/operations/delete-v2-permission-3cd9a1fce9f8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "permission_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/permission"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "permission_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/permission" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"permission_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_permission","summary":"Berechtigungsprofil entfernen","description":"Entfernt ein eigenes, unbenutztes Berechtigungsprofil. Systemprofile sind gesperrt; bei noch zugeordneten Administratoren oder API-Zugängen bleibt delete=false mit error_code=is_still_in_use.","tags":["permission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"permission_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Berechtigungsprofil entfernen","value":{"id":"permission_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Permission.php","method":"deletePermission","line":179},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"permission","short":"perm","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/permission Berechtigungsprofile auflisten Liest die im gewählten Konto verfügbaren Berechtigungsprofile. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft. - Operation-ID: get_v2_permission - Dokumentation: /docs/api/v2/operations/get-v2-permission-7f162e6de9e1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/permission"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/permission" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_permission","summary":"Berechtigungsprofile auflisten","description":"Liest die im gewählten Konto verfügbaren Berechtigungsprofile. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft.","tags":["permission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"permission_012345abcdef_123456":{"id":"permission_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Permission.php","method":"getPermission","line":227},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"permission","short":"perm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/permission Berechtigungsprofil anlegen Legt ein eigenes Profil mit name an. Ressourcenrechte werden unter den Ressourcennamen der Konto-Konfiguration übergeben: denied, read, write, readwrite oder readwritedelete. default=true löst das vorherige Standardprofil ab; acl und Erstellzeit werden serverseitig gesetzt. - Operation-ID: post_v2_permission - Dokumentation: /docs/api/v2/operations/post-v2-permission-92bfd8875d84/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Veranstaltungsverwaltung", "offer": "readwrite", "booking": "read", "default": false } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/permission"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Veranstaltungsverwaltung", "offer": "readwrite", "booking": "read", "default": false } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/permission" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Veranstaltungsverwaltung\",\"offer\":\"readwrite\",\"booking\":\"read\",\"default\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_permission","summary":"Berechtigungsprofil anlegen","description":"Legt ein eigenes Profil mit name an. Ressourcenrechte werden unter den Ressourcennamen der Konto-Konfiguration übergeben: denied, read, write, readwrite oder readwritedelete. default=true löst das vorherige Standardprofil ab; acl und Erstellzeit werden serverseitig gesetzt.","tags":["permission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"permission_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":35},"acl":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":48,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"default":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PUT treats only the literal JSON false as unset. Every other JSON value requests true. Unsetting the current default is refused. POST uses loose PHP comparison to true; an inserted default clears other defaults through _insert_func.","description":"PUT treats only the literal JSON false as unset. Every other JSON value requests true. Unsetting the current default is refused. POST uses loose PHP comparison to true; an inserted default clears other defaults through _insert_func.","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"create_time":{"not":{},"description":"Server-generated creation timestamp. POST ignores the supplied value and uses time(); single-record insert_default supplies the field when omitted. PUT skips update=false.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"offer":{"description":"Ressourcenrecht für Angebote. Kanonische Werte: denied, read, write, readwrite, readwritedelete; der native Normalisierer setzt andere Werte auf denied.","examples":["readwrite"]},"booking":{"description":"Ressourcenrecht für Buchungen; gleiche Normalisierung wie offer.","examples":["read"]}},"additionalProperties":true,"description":"Offenes Profilobjekt. Zusätzliche Ressourcenfelder stammen aus api_permission; Werte denied/read/write/readwrite/readwritedelete werden durch dpP normalisiert, unbekannte Werte zu denied. Keine erfundenen Pflichtfelder.","required":["name"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":35},"acl":{"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":48,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"default":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PUT treats only the literal JSON false as unset. Every other JSON value requests true. Unsetting the current default is refused. POST uses loose PHP comparison to true; an inserted default clears other defaults through _insert_func.","description":"PUT treats only the literal JSON false as unset. Every other JSON value requests true. Unsetting the current default is refused. POST uses loose PHP comparison to true; an inserted default clears other defaults through _insert_func.","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"create_time":{"not":{},"description":"Server-generated creation timestamp. POST ignores the supplied value and uses time(); single-record insert_default supplies the field when omitted. PUT skips update=false.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"offer":{"description":"Ressourcenrecht für Angebote. Kanonische Werte: denied, read, write, readwrite, readwritedelete; der native Normalisierer setzt andere Werte auf denied.","examples":["readwrite"]},"booking":{"description":"Ressourcenrecht für Buchungen; gleiche Normalisierung wie offer.","examples":["read"]}},"additionalProperties":true,"description":"Offenes Profilobjekt. Zusätzliche Ressourcenfelder stammen aus api_permission; Werte denied/read/write/readwrite/readwritedelete werden durch dpP normalisiert, unbekannte Werte zu denied. Keine erfundenen Pflichtfelder.","required":["name"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Berechtigungsprofil anlegen","value":{"data":{"name":"Veranstaltungsverwaltung","offer":"readwrite","booking":"read","default":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Permission.php","method":"postPermission","line":112},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"permission","short":"perm","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/permission Berechtigungsprofil ändern Ändert name, default oder konfigurierte Ressourcenrechte eines eigenen Profils. Systemprofile mit acl>0 oder acl=-1 sind gesperrt. default=false kann das bestehende Standardprofil nicht entfernen; Ressourcenrechte verwenden denied/read/write/readwrite/readwritedelete. Die öffentliche Ziel-ID muss als id im JSON-Request angegeben werden. - Operation-ID: put_v2_permission - Dokumentation: /docs/api/v2/operations/put-v2-permission-1243edfcb367/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "permission_012345abcdef_123456", "data": { "name": "Veranstaltungsverwaltung" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/permission' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/permission"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "permission_012345abcdef_123456", "data": { "name": "Veranstaltungsverwaltung" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/permission" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"permission_012345abcdef_123456\",\"data\":{\"name\":\"Veranstaltungsverwaltung\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_permission","summary":"Berechtigungsprofil ändern","description":"Ändert name, default oder konfigurierte Ressourcenrechte eines eigenen Profils. Systemprofile mit acl>0 oder acl=-1 sind gesperrt. default=false kann das bestehende Standardprofil nicht entfernen; Ressourcenrechte verwenden denied/read/write/readwrite/readwritedelete. Die öffentliche Ziel-ID muss als id im JSON-Request angegeben werden.","tags":["permission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"permission_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":35},"acl":false,"default":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PUT treats only the literal JSON false as unset. Every other JSON value requests true. Unsetting the current default is refused. POST uses loose PHP comparison to true; an inserted default clears other defaults through _insert_func.","description":"PUT treats only the literal JSON false as unset. Every other JSON value requests true. Unsetting the current default is refused. POST uses loose PHP comparison to true; an inserted default clears other defaults through _insert_func.","examples":[true],"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"create_time":false,"offer":{"description":"Ressourcenrecht für Angebote. Kanonische Werte: denied, read, write, readwrite, readwritedelete; der native Normalisierer setzt andere Werte auf denied.","examples":["readwrite"]},"booking":{"description":"Ressourcenrecht für Buchungen; gleiche Normalisierung wie offer.","examples":["read"]}},"additionalProperties":true,"description":"Offenes Profilobjekt. Zusätzliche Ressourcenfelder stammen aus api_permission; Werte denied/read/write/readwrite/readwritedelete werden durch dpP normalisiert, unbekannte Werte zu denied. Keine erfundenen Pflichtfelder.","minProperties":1,"x-edoobox-readOnlyFields":["acl","create_time"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Berechtigungsprofil ändern","value":{"id":"permission_012345abcdef_123456","data":{"name":"Veranstaltungsverwaltung"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Permission.php","method":"updatePermission","line":130},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"permission","short":"perm","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/permission/{id} Berechtigungsprofil entfernen Entfernt ein eigenes, unbenutztes Berechtigungsprofil. Systemprofile sind gesperrt; bei noch zugeordneten Administratoren oder API-Zugängen bleibt delete=false mit error_code=is_still_in_use. - Operation-ID: delete_v2_permission_id - Dokumentation: /docs/api/v2/operations/delete-v2-permission-id-814405365561/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/permission/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/permission/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/permission/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_permission_id","summary":"Berechtigungsprofil entfernen","description":"Entfernt ein eigenes, unbenutztes Berechtigungsprofil. Systemprofile sind gesperrt; bei noch zugeordneten Administratoren oder API-Zugängen bleibt delete=false mit error_code=is_still_in_use.","tags":["permission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"permission_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^permission_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"examples":{"request":{"summary":"Berechtigungsprofil entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Permission.php","method":"deletePermission","line":179},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"permission","short":"perm","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/permission/{id} Berechtigungsprofil lesen Liest das durch die öffentliche Pfad-ID gewählte Berechtigungsprofil. Auch ein Einzelabruf liefert die normale GET-Datenstruktur; Feldauswahl, Filter und Konto-Berechtigungen bleiben wirksam. - Operation-ID: get_v2_permission_id - Dokumentation: /docs/api/v2/operations/get-v2-permission-id-72c2a05ba817/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/permission/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/permission/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/permission/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_permission_id","summary":"Berechtigungsprofil lesen","description":"Liest das durch die öffentliche Pfad-ID gewählte Berechtigungsprofil. Auch ein Einzelabruf liefert die normale GET-Datenstruktur; Feldauswahl, Filter und Konto-Berechtigungen bleiben wirksam.","tags":["permission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"permission_012345abcdef_123456":{"id":"permission_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Permission.php","method":"getPermission","line":227},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"permission","short":"perm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/permission/{id} Berechtigungsprofil ändern Ändert name, default oder konfigurierte Ressourcenrechte eines eigenen Profils. Systemprofile mit acl>0 oder acl=-1 sind gesperrt. default=false kann das bestehende Standardprofil nicht entfernen; Ressourcenrechte verwenden denied/read/write/readwrite/readwritedelete. Die öffentliche Ziel-ID steht im Pfad. - Operation-ID: put_v2_permission_id - Dokumentation: /docs/api/v2/operations/put-v2-permission-id-4147c6e23ffe/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/permission/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/permission/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = readFileSync("payload.json"); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/permission/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = Path("payload.json").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_permission_id","summary":"Berechtigungsprofil ändern","description":"Ändert name, default oder konfigurierte Ressourcenrechte eines eigenen Profils. Systemprofile mit acl>0 oder acl=-1 sind gesperrt. default=false kann das bestehende Standardprofil nicht entfernen; Ressourcenrechte verwenden denied/read/write/readwrite/readwritedelete. Die öffentliche Ziel-ID steht im Pfad.","tags":["permission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"permission_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":35},"acl":false,"default":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PUT treats only the literal JSON false as unset. Every other JSON value requests true. Unsetting the current default is refused. POST uses loose PHP comparison to true; an inserted default clears other defaults through _insert_func.","description":"PUT treats only the literal JSON false as unset. Every other JSON value requests true. Unsetting the current default is refused. POST uses loose PHP comparison to true; an inserted default clears other defaults through _insert_func.","examples":[true],"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"create_time":false,"offer":{"description":"Ressourcenrecht für Angebote. Kanonische Werte: denied, read, write, readwrite, readwritedelete; der native Normalisierer setzt andere Werte auf denied.","examples":["readwrite"]},"booking":{"description":"Ressourcenrecht für Buchungen; gleiche Normalisierung wie offer.","examples":["read"]}},"additionalProperties":true,"description":"Offenes Profilobjekt. Zusätzliche Ressourcenfelder stammen aus api_permission; Werte denied/read/write/readwrite/readwritedelete werden durch dpP normalisiert, unbekannte Werte zu denied. Keine erfundenen Pflichtfelder.","minProperties":1,"x-edoobox-readOnlyFields":["acl","create_time"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Permission.php","method":"updatePermission","line":130},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"permission","short":"perm","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/permission/list Berechtigungsprofile auflisten Liest die im gewählten Konto verfügbaren Berechtigungsprofile. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft. - Operation-ID: get_v2_permission_list - Dokumentation: /docs/api/v2/operations/get-v2-permission-list-77e8d66405d1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/permission/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/permission/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/permission/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_permission_list","summary":"Berechtigungsprofile auflisten","description":"Liest die im gewählten Konto verfügbaren Berechtigungsprofile. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft.","tags":["permission"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"permission_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"permission_012345abcdef_123456":{"id":"permission_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":35,"type":"string"},"acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":48,"type":"integer"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":66,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permissionfunc.php","line":6,"reason":"Strict false branching and current default protection.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":38,"reason":"Account default lookup/fallback; source is read only for this review.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permissionfunc.php","line":83,"reason":"POST inserted default clears other account defaults.","sha256":"feacb9fc50ad276cd80c86f5ddb8f07176365a6aeda494d64a7b594080e0bf70"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Editable custom-permission target check before generic PUT.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"create_time":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Permission.php","line":76,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":112,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"application/models/v2/Permission.php","line":130,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"61d1a74f5ffce21d1eaddfe65588fd6bb8b809d63e748a7536765e66ad7abead"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Permission.php","method":"getPermission","line":227},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"permission","short":"perm","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/place Veranstaltungsort löschen Löscht Veranstaltungsort im gewählten Konto. Eine öffentliche Ziel-ID ist erforderlich: als id im JSON-Umschlag oder als gleichnamiger Query-Parameter. Ein JSON-id hat Vorrang. Der Veranstaltungsort wird als gelöscht markiert (iamdelete); es findet keine physische Löschung des Datensatzes statt. - Operation-ID: delete_v2_place - Dokumentation: /docs/api/v2/operations/delete-v2-place-0c8e23f787cc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "place_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/place' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "place_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"place_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_place","summary":"Veranstaltungsort löschen","description":"Löscht Veranstaltungsort im gewählten Konto. Eine öffentliche Ziel-ID ist erforderlich: als id im JSON-Umschlag oder als gleichnamiger Query-Parameter. Ein JSON-id hat Vorrang. Der Veranstaltungsort wird als gelöscht markiert (iamdelete); es findet keine physische Löschung des Datensatzes statt.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"place_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Veranstaltungsort löschen","value":{"id":"place_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"deletePlace","line":203},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/place Veranstaltungsorte auflisten Liest Veranstaltungsorte im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. - Operation-ID: get_v2_place - Dokumentation: /docs/api/v2/operations/get-v2-place-2024ee10d15d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/place' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_place","summary":"Veranstaltungsorte auflisten","description":"Liest Veranstaltungsorte im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"place_012345abcdef_123456":{"id":"place_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"getPlace","line":225},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/place Veranstaltungsort anlegen Legt Veranstaltungsort mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. Änderungen an Adressfeldern bzw. automatic_positioning können die geografische Position des Orts neu bestimmen. - Operation-ID: post_v2_place - Dokumentation: /docs/api/v2/operations/post-v2-place-8e540e55d838/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/place' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_place","summary":"Veranstaltungsort anlegen","description":"Legt Veranstaltungsort mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. Änderungen an Adressfeldern bzw. automatic_positioning können die geografische Position des Orts neu bestimmen.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"place_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":47},"shortcut":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":60},"lat":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":69},"long":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":78},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":87},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":97},"town":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":107},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":117},"automatic_positioning":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":47},"shortcut":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":60},"lat":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":69},"long":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":78},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":87},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":97},"town":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":107},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":117},"automatic_positioning":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"postPlace","line":157},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/place Veranstaltungsort aktualisieren Ändert die übergebenen schreibbaren Felder in data für Veranstaltungsort. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. Änderungen an Adressfeldern bzw. automatic_positioning können die geografische Position des Orts neu bestimmen. - Operation-ID: put_v2_place - Dokumentation: /docs/api/v2/operations/put-v2-place-2871d5923f31/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" }, "id": "place_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/place' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" }, "id": "place_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"},\"id\":\"place_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_place","summary":"Veranstaltungsort aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Veranstaltungsort. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. Änderungen an Adressfeldern bzw. automatic_positioning können die geografische Position des Orts neu bestimmen.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"place_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":47},"shortcut":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":60},"lat":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":69},"long":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":78},"street":{"x-edoobox-canonicalType":"string","description":"Inbound aP(value,true) normalizes strings: trim, flatten linebreaks, escape, truncate the legacy encoded storage prefix to at most120 characters without splitting the final escape pair. Nonstring values become empty string. Longer/nonstring input is normalized, not rejected for type/length.\n\nFinal hook calls inbound aP(...,true), not output aQ. All supplied address/country/automatic fields are aggregated regardless of payload order; requested automatic flag controls optional geocoding from the complete merged address.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update.","x-edoobox-schemaStatus":"source-reviewed","examples":["Example Street 5"],"x-edoobox-maxStoredLength":120,"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"postcode":{"x-edoobox-canonicalType":"string","description":"Inbound aP(value,true) normalizes strings: trim, flatten linebreaks, escape, truncate the legacy encoded storage prefix to at most20 characters without splitting the final escape pair. Nonstring values become empty string. Longer/nonstring input is normalized, not rejected for type/length.\n\nFinal hook calls inbound aP(...,true), not output aQ. All supplied address/country/automatic fields are aggregated regardless of payload order; requested automatic flag controls optional geocoding from the complete merged address.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update.","x-edoobox-schemaStatus":"source-reviewed","examples":["1000"],"x-edoobox-maxStoredLength":20,"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"town":{"x-edoobox-canonicalType":"string","description":"Inbound aP(value,true) normalizes strings: trim, flatten linebreaks, escape, truncate the legacy encoded storage prefix to at most120 characters without splitting the final escape pair. Nonstring values become empty string. Longer/nonstring input is normalized, not rejected for type/length.\n\nFinal hook calls inbound aP(...,true), not output aQ. All supplied address/country/automatic fields are aggregated regardless of payload order; requested automatic flag controls optional geocoding from the complete merged address.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update.","x-edoobox-schemaStatus":"source-reviewed","examples":["Example Town"],"x-edoobox-maxStoredLength":120,"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Valid global country v2 ID with correct country prefix/hash and positive decoded country value. Raw IDs, wrong prefixes/tampered IDs, null/false reject ED80100. Country IDs are global and valid across accounts; do not impose tenant-country ownership.\n\nFinal hook calls the declared inbound decoder with checkType=country. Positive decoded value retained; other inputs return strictfalse and throw ED80100. ID is global: it need not be reissued for the selected account. Output re-encodes country as opaque global v2 ID.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes. Country prefix/hash/decoded-positive validation failure -> ED80100 fieldcountry. No tenant-country restriction: Helper Api treats country as global; account comparison is for target place, not global country.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update. A decoded positive country with no matching lander row may follow the existing automatic-positioning fallback; no additional existence validation is claimed.","x-edoobox-schemaStatus":"source-reviewed","examples":["country_012345abcdef_123456"],"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"automatic_positioning":{"x-edoobox-canonicalType":"boolean","description":"Inbound value==true ->1 else0. Canonical booleans supported; PHP loose truth conversion accepts broader values. New supplied flag overrides stored state before geocoding.\n\nFinal hook invokes input get, aggregating all address fields. false disables geocoding even if previously enabled; true enables it on the complete merged address even if previously disabled.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update.","x-edoobox-schemaStatus":"source-reviewed","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Veranstaltungsort aktualisieren","value":{"data":{"name":"Example"},"id":"place_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"updatePlace","line":180},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/place/{id} Veranstaltungsort löschen Löscht Veranstaltungsort im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Der Veranstaltungsort wird als gelöscht markiert (iamdelete); es findet keine physische Löschung des Datensatzes statt. - Operation-ID: delete_v2_place_id - Dokumentation: /docs/api/v2/operations/delete-v2-place-id-ca32ab42accf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "place_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/place/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "place_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"place_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_place_id","summary":"Veranstaltungsort löschen","description":"Löscht Veranstaltungsort im gewählten Konto. Die Ziel-ID kommt aus dem Pfad; ein JSON-id überschreibt sie. Der Veranstaltungsort wird als gelöscht markiert (iamdelete); es findet keine physische Löschung des Datensatzes statt.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"place_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Veranstaltungsort löschen","value":{"id":"place_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"deletePlace","line":203},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/place/{id} Veranstaltungsort abrufen Liest Veranstaltungsort anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. - Operation-ID: get_v2_place_id - Dokumentation: /docs/api/v2/operations/get-v2-place-id-17f530c2be3a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/place/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_place_id","summary":"Veranstaltungsort abrufen","description":"Liest Veranstaltungsort anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"place_012345abcdef_123456":{"id":"place_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"getPlace","line":225},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/place/{id} Veranstaltungsort aktualisieren Ändert die übergebenen schreibbaren Felder in data für Veranstaltungsort. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. Änderungen an Adressfeldern bzw. automatic_positioning können die geografische Position des Orts neu bestimmen. - Operation-ID: put_v2_place_id - Dokumentation: /docs/api/v2/operations/put-v2-place-id-80d0481d20e8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/place/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_place_id","summary":"Veranstaltungsort aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Veranstaltungsort. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. Änderungen an Adressfeldern bzw. automatic_positioning können die geografische Position des Orts neu bestimmen.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"place_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":47},"shortcut":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":60},"lat":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":69},"long":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":78},"street":{"x-edoobox-canonicalType":"string","description":"Inbound aP(value,true) normalizes strings: trim, flatten linebreaks, escape, truncate the legacy encoded storage prefix to at most120 characters without splitting the final escape pair. Nonstring values become empty string. Longer/nonstring input is normalized, not rejected for type/length.\n\nFinal hook calls inbound aP(...,true), not output aQ. All supplied address/country/automatic fields are aggregated regardless of payload order; requested automatic flag controls optional geocoding from the complete merged address.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update.","x-edoobox-schemaStatus":"source-reviewed","examples":["Example Street 5"],"x-edoobox-maxStoredLength":120,"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"x-edoobox-canonicalType":"string","description":"Inbound aP(value,true) normalizes strings: trim, flatten linebreaks, escape, truncate the legacy encoded storage prefix to at most20 characters without splitting the final escape pair. Nonstring values become empty string. Longer/nonstring input is normalized, not rejected for type/length.\n\nFinal hook calls inbound aP(...,true), not output aQ. All supplied address/country/automatic fields are aggregated regardless of payload order; requested automatic flag controls optional geocoding from the complete merged address.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update.","x-edoobox-schemaStatus":"source-reviewed","examples":["1000"],"x-edoobox-maxStoredLength":20,"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"x-edoobox-canonicalType":"string","description":"Inbound aP(value,true) normalizes strings: trim, flatten linebreaks, escape, truncate the legacy encoded storage prefix to at most120 characters without splitting the final escape pair. Nonstring values become empty string. Longer/nonstring input is normalized, not rejected for type/length.\n\nFinal hook calls inbound aP(...,true), not output aQ. All supplied address/country/automatic fields are aggregated regardless of payload order; requested automatic flag controls optional geocoding from the complete merged address.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update.","x-edoobox-schemaStatus":"source-reviewed","examples":["Example Town"],"x-edoobox-maxStoredLength":120,"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Valid global country v2 ID with correct country prefix/hash and positive decoded country value. Raw IDs, wrong prefixes/tampered IDs, null/false reject ED80100. Country IDs are global and valid across accounts; do not impose tenant-country ownership.\n\nFinal hook calls the declared inbound decoder with checkType=country. Positive decoded value retained; other inputs return strictfalse and throw ED80100. ID is global: it need not be reissued for the selected account. Output re-encodes country as opaque global v2 ID.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes. Country prefix/hash/decoded-positive validation failure -> ED80100 fieldcountry. No tenant-country restriction: Helper Api treats country as global; account comparison is for target place, not global country.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update. A decoded positive country with no matching lander row may follow the existing automatic-positioning fallback; no additional existence validation is claimed.","x-edoobox-schemaStatus":"source-reviewed","examples":["country_012345abcdef_123456"],"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"x-edoobox-canonicalType":"boolean","description":"Inbound value==true ->1 else0. Canonical booleans supported; PHP loose truth conversion accepts broader values. New supplied flag overrides stored state before geocoding.\n\nFinal hook invokes input get, aggregating all address fields. false disables geocoding even if previously enabled; true enables it on the complete merged address even if previously disabled.\n\nRuntime preconditions / rejection: Generic writer checks target ownership before hook. Hook additionally refuses a missing place in selected account with ED80020 before geocoding. Any supplied shared field whose inbound getter returns strict false throws ED80100 with field name before address/geocoder reads or writes.\n\nScope / limitations: Geolocation availability/precision and live SQL behavior were not exercised by this source-only review. No longer order-sensitive across the five shared address fields; hook aggregates complete payload before one geolocation update.","x-edoobox-schemaStatus":"source-reviewed","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"updatePlace","line":180},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/place/{id}/notification Ortsbenachrichtigung löschen Löscht eine Benachrichtigungsregel physisch. Die Ziel-ID ist eine placenotification-ID, keine Orts-ID. Sie steht im Pfad; ein JSON-id kann sie überschreiben. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: delete_v2_place_id_notification - Dokumentation: /docs/api/v2/operations/delete-v2-place-id-notification-b3f08c0c2c40/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche placenotification-ID im gewählten Konto. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"description":"Öffentliche placenotification-ID im gewählten Konto."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "placenotification_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/place/'"${PATH_ID}"'/notification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/{id}/notification"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "placenotification_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/{id}/notification" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"placenotification_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_place_id_notification","summary":"Ortsbenachrichtigung löschen","description":"Löscht eine Benachrichtigungsregel physisch. Die Ziel-ID ist eine placenotification-ID, keine Orts-ID. Sie steht im Pfad; ein JSON-id kann sie überschreiben. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"description":"Öffentliche placenotification-ID im gewählten Konto."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"placenotification_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Ortsbenachrichtigung löschen","value":{"id":"placenotification_012345abcdef_123456"}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Place.php","line":209,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"deletePlace","line":203,"branch":{"file":"application/models/v2/Place.php","line":209}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/place/{id}/notification Ortsbenachrichtigung abrufen Liest Benachrichtigungsregeln für Veranstaltungsorte. id bezeichnet die Benachrichtigungsregel (placenotification), nicht den Veranstaltungsort. - Operation-ID: get_v2_place_id_notification - Dokumentation: /docs/api/v2/operations/get-v2-place-id-notification-786c8e61007d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche placenotification-ID im gewählten Konto. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"description":"Öffentliche placenotification-ID im gewählten Konto."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/place/'"${PATH_ID}"'/notification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/{id}/notification"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/{id}/notification" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_place_id_notification","summary":"Ortsbenachrichtigung abrufen","description":"Liest Benachrichtigungsregeln für Veranstaltungsorte. id bezeichnet die Benachrichtigungsregel (placenotification), nicht den Veranstaltungsort.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"description":"Öffentliche placenotification-ID im gewählten Konto."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"placenotification_012345abcdef_123456":{"id":"placenotification_012345abcdef_123456","place":"place_012345abcdef_123456","name":"Raumbelegung","email":["planung@example.invalid"],"rule_type":"days_before","run_hour":8,"rule_days_before":2}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"place":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"},"email":{"type":"array","items":{"type":"string"}},"rule_type":{"type":"string"},"run_hour":{"type":"integer"},"run_weekday":{"type":"integer"},"rule_days_before":{"type":"integer"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"place":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"},"email":{"type":"array","items":{"type":"string"}},"rule_type":{"type":"string"},"run_hour":{"type":"integer"},"run_weekday":{"type":"integer"},"rule_days_before":{"type":"integer"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true}}],"description":"Nach öffentlichen v2-IDs indiziertes Objekt; response_type=array liefert eine Liste. Eine leere Ergebnismenge kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Place.php","line":235,"reason":"The explicit segment4 notification branch selects NotificationStructure and its placenotification ID filter. The inverted Vue /place/notification/{id} URL is a separate client error."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Place.php","line":235},"reason":"getPlace selects NotificationStructure then GET_data; all response setters only format scalars, split email addresses, and encode v2 IDs. No write hook or external call is reached by this read.","relatedSources":[{"file":"application/models/v2/Place.php","line":250}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"getPlace","line":225,"branch":{"file":"application/models/v2/Place.php","line":235}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/place/{id}/notification Ortsbenachrichtigung aktualisieren Ändert die übergebenen Regel-Felder. place bleibt unveränderlich. Die Pfad-ID bezeichnet die placenotification-Regel. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID. - Operation-ID: put_v2_place_id_notification - Dokumentation: /docs/api/v2/operations/put-v2-place-id-notification-5984613e39f8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche placenotification-ID im gewählten Konto. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"description":"Öffentliche placenotification-ID im gewählten Konto."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "run_hour": 9 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/place/'"${PATH_ID}"'/notification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/{id}/notification"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "run_hour": 9 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/{id}/notification" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"run_hour\":9}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_place_id_notification","summary":"Ortsbenachrichtigung aktualisieren","description":"Ändert die übergebenen Regel-Felder. place bleibt unveränderlich. Die Pfad-ID bezeichnet die placenotification-Regel. Ein optionales id im JSON-Umschlag überschreibt die Pfad-ID.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"description":"Öffentliche placenotification-ID im gewählten Konto."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"placenotification_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"E-Mail-Liste oder kommagetrennte Zeichenfolge; Leerzeichen und ungültige Adressen werden entfernt."},"rule_type":{"description":"weekday oder days_before; andere Werte werden zu days_before normalisiert.","examples":["days_before","weekday"],"x-edoobox-canonicalType":"string"},"run_hour":{"x-edoobox-canonicalType":"integer","description":"PHP intval-Normalisierung."},"run_weekday":{"x-edoobox-canonicalType":"integer","description":"PHP intval; Werte außerhalb 0 bis 6 werden auf 0 normalisiert."},"rule_days_before":{"x-edoobox-canonicalType":"integer","description":"PHP intval-Normalisierung."},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true},"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","examples":["placenotification_012345abcdef_123456"],"description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Ortsbenachrichtigung aktualisieren","value":{"data":{"run_hour":9}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Place.php","line":187,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"updatePlace","line":180,"branch":{"file":"application/models/v2/Place.php","line":187}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/place/{id}/workload Belegung für Veranstaltungsort abrufen Liefert eine Tabelle der Termine, die den gewünschten Zeitraum überlappen. filter benötigt start und end als parsebare Datumswerte. Die Pfad-ID wird als erster id-Filter eingesetzt. Gelöschte bzw. im Papierkorb befindliche Angebote werden ausgeschlossen. Die Zeilen entsprechen der Reihenfolge in columns. - Operation-ID: get_v2_place_id_workload - Dokumentation: /docs/api/v2/operations/get-v2-place-id-workload-5a8b97a82d2d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Pflichtfilter: start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Pflichtfilter: start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/place/'"${PATH_ID}"'/workload' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/{id}/workload"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/{id}/workload" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_place_id_workload","summary":"Belegung für Veranstaltungsort abrufen","description":"Liefert eine Tabelle der Termine, die den gewünschten Zeitraum überlappen. filter benötigt start und end als parsebare Datumswerte. Die Pfad-ID wird als erster id-Filter eingesetzt. Gelöschte bzw. im Papierkorb befindliche Angebote werden ausgeschlossen. Die Zeilen entsprechen der Reihenfolge in columns.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Pflichtfilter: start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"offername","type":"string"},{"field":"name","type":"string"},{"field":"offer_id","type":"string"},{"field":"date_id","type":"string"},{"field":"offerdef_id","type":"string"},{"field":"place_id","type":"string"},{"field":"room_id","type":"string"},{"field":"status","type":"integer"},{"field":"date_start","type":"date"},{"field":"date_end","type":"date"}],"rows":[["Beispielangebot","Einführung","offer_012345abcdef_123456","date_012345abcdef_123456","offerdef_012345abcdef_123456","place_012345abcdef_123456","room_012345abcdef_123456",1,"2026-11-03T09:00:00+01:00","2026-11-03T10:00:00+01:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},{"type":"string","pattern":"^offerdef_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerdef_012345abcdef_123456"]},{"anyOf":[{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},{"const":false}]},{"anyOf":[{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},{"const":false}]},{"type":"integer"},{"anyOf":[{"type":"string"},{"const":false}]},{"anyOf":[{"type":"string"},{"const":false}]}],"minItems":10,"maxItems":10}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Place.php","line":238,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminfunc.php","line":172},"reason":"Workload SELECT and formatting only; Placefunc194/Roomfunc12 likewise."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"getPlace","line":225,"branch":{"file":"application/models/v2/Place.php","line":238}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/place/list Veranstaltungsorte auflisten Liest Veranstaltungsorte im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. - Operation-ID: get_v2_place_list - Dokumentation: /docs/api/v2/operations/get-v2-place-list-a3abb934b3a1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/place/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_place_list","summary":"Veranstaltungsorte auflisten","description":"Liest Veranstaltungsorte im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"place_012345abcdef_123456":{"id":"place_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"type":"string"},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":60,"type":"string"},"lat":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":69,"type":"string"},"long":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":78,"type":"string"},"street":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":87,"lineStart":87,"lineEnd":96,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"5a6bf98eca49df40f27438974f92a85486e29811c3774ff99e44c3edbf1299e4","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"postcode":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":97,"lineStart":97,"lineEnd":106,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"39515d089e7e032f637a9a1eaa57182cd76f2a781057eefdd2c29130fff92f72","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"town":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":107,"lineStart":107,"lineEnd":116,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9b657f3f3e23f190502c1869e4561c1a728d1f80d0ef4937f88b5424164b9bb2","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11606,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"2172cc49e4bcdb6840448b62929037cb9b90c299da3cb6fb0be6a8782881750f","reason":"Actual aP inbound string/nonstring handling and aQ outbound conversion. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":117,"lineStart":117,"lineEnd":130,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"70c9e92d6ef5d8263f0609437598b8a89b475fad34b930847ca8da1dc58b0bb9","reason":"Structure getter, output setter and field flags."},{"file":"application/libraries/edoobox/helper/Api.php","line":8,"lineStart":8,"lineEnd":106,"sha256":"b22e6f0474a07f356b625fe20e8b1d15fa87a104f9c155e04978e4c60c8df9fd","spanSha256":"9845d13d49fb8d4ca3934aca41a2563ccdb40a3c24b35ad926413ed493ed4816","reason":"Actual ID encoder returns type_nan_0 for nonnumeric inputs; decoder is bypassed."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"automatic_positioning":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Place.php","line":131,"lineStart":131,"lineEnd":140,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"9d20b93ec324cbd68a1815f3bf957f3c97b8da4aac409cb6f21ea99ce082725b","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Place.php","line":157,"lineStart":157,"lineEnd":193,"sha256":"410cf012c1c4f231f46ce98547f69fd93925176cfc07cced44b330fcfefb87c1","spanSha256":"e1d9787645b3b99996e0e93c07b8a9f73fd5f8575936077f169c25f1db6237c4","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"application/models/v2/Placefunc.php","line":77,"lineStart":77,"lineEnd":97,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"345667d8fdafd0ac9005a07f197967525a4de48d0d59f0013f49223095f40931","reason":"Final fixed shared hook aggregates every supplied field assigned to this callable, invokes inbound get(value,fid,true), rejects strict false before address/geocoder reads or writes."},{"file":"application/models/v2/Placefunc.php","line":99,"lineStart":99,"lineEnd":169,"sha256":"e1a54837896d2bc7a5448f59b2a9dfa9e223390af14097707b5ad8662550f939","spanSha256":"be93cef9daf5ea84d2bed11a5ad567e4fcf372b67c1ccca622c9d2a1d7f8a83d","reason":"Final fixed owned place query requires id+fid, throws ED80020 when absent, merges all normalized address components, uses requested automatic-positioning state before geocoding."},{"file":"application/models/v2/API_DATA.php","line":1760,"lineStart":1760,"lineEnd":1884,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","spanSha256":"eadc56a4dc19ac584c53b2a0c3afa15949c7320db4a7b0a2a8c34ef82f170ab5","reason":"Actual generic update decodes target and checks ownership before shared callback; callback runs once but now aggregates all five fields. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"getPlace","line":225},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/place/list/notification Ortsbenachrichtigung löschen Löscht eine Benachrichtigungsregel physisch. Die Ziel-ID ist eine placenotification-ID, keine Orts-ID. id muss im JSON-Umschlag oder als Query-Parameter übergeben werden. - Operation-ID: delete_v2_place_list_notification - Dokumentation: /docs/api/v2/operations/delete-v2-place-list-notification-3326fe2142b4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Öffentliche placenotification-ID; erforderlich, wenn kein JSON-id gesendet wird. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"description":"Öffentliche placenotification-ID; erforderlich, wenn kein JSON-id gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "placenotification_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/place/list/notification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/list/notification"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "placenotification_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/list/notification" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"placenotification_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_place_list_notification","summary":"Ortsbenachrichtigung löschen","description":"Löscht eine Benachrichtigungsregel physisch. Die Ziel-ID ist eine placenotification-ID, keine Orts-ID. id muss im JSON-Umschlag oder als Query-Parameter übergeben werden.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"description":"Öffentliche placenotification-ID; erforderlich, wenn kein JSON-id gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"placenotification_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Ortsbenachrichtigung löschen","value":{"id":"placenotification_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"deletePlace","line":203,"branch":{"file":"application/models/v2/Place.php","line":209}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/place/list/notification Ortsbenachrichtigungen auflisten Liest Benachrichtigungsregeln für Veranstaltungsorte. Mit einem Filter auf place lassen sich Regeln für einen Veranstaltungsort auswählen. - Operation-ID: get_v2_place_list_notification - Dokumentation: /docs/api/v2/operations/get-v2-place-list-notification-88e2fe30fd07/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/place/list/notification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/list/notification"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/list/notification" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_place_list_notification","summary":"Ortsbenachrichtigungen auflisten","description":"Liest Benachrichtigungsregeln für Veranstaltungsorte. Mit einem Filter auf place lassen sich Regeln für einen Veranstaltungsort auswählen.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"place_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"placenotification_012345abcdef_123456":{"id":"placenotification_012345abcdef_123456","place":"place_012345abcdef_123456","name":"Raumbelegung","email":["planung@example.invalid"],"rule_type":"days_before","run_hour":8,"rule_days_before":2}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"place":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"},"email":{"type":"array","items":{"type":"string"}},"rule_type":{"type":"string"},"run_hour":{"type":"integer"},"run_weekday":{"type":"integer"},"rule_days_before":{"type":"integer"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"place":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"},"email":{"type":"array","items":{"type":"string"}},"rule_type":{"type":"string"},"run_hour":{"type":"integer"},"run_weekday":{"type":"integer"},"rule_days_before":{"type":"integer"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true}}],"description":"Nach öffentlichen v2-IDs indiziertes Objekt; response_type=array liefert eine Liste. Eine leere Ergebnismenge kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Place.php","line":235},"reason":"getPlace selects NotificationStructure then GET_data; all response setters only format scalars, split email addresses, and encode v2 IDs. No write hook or external call is reached by this read.","relatedSources":[{"file":"application/models/v2/Place.php","line":250}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"getPlace","line":225,"branch":{"file":"application/models/v2/Place.php","line":235}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/place/list/notification Ortsbenachrichtigung aktualisieren Ändert die übergebenen Regel-Felder. place bleibt unveränderlich. Die placenotification-ID ist als id im JSON-Umschlag erforderlich. - Operation-ID: put_v2_place_list_notification - Dokumentation: /docs/api/v2/operations/put-v2-place-list-notification-841e390bd990/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "run_hour": 9 }, "id": "placenotification_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/place/list/notification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/list/notification"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "run_hour": 9 }, "id": "placenotification_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/list/notification" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"run_hour\":9},\"id\":\"placenotification_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_place_list_notification","summary":"Ortsbenachrichtigung aktualisieren","description":"Ändert die übergebenen Regel-Felder. place bleibt unveränderlich. Die placenotification-ID ist als id im JSON-Umschlag erforderlich.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"placenotification_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"E-Mail-Liste oder kommagetrennte Zeichenfolge; Leerzeichen und ungültige Adressen werden entfernt."},"rule_type":{"description":"weekday oder days_before; andere Werte werden zu days_before normalisiert.","examples":["days_before","weekday"],"x-edoobox-canonicalType":"string"},"run_hour":{"x-edoobox-canonicalType":"integer","description":"PHP intval-Normalisierung."},"run_weekday":{"x-edoobox-canonicalType":"integer","description":"PHP intval; Werte außerhalb 0 bis 6 werden auf 0 normalisiert."},"rule_days_before":{"x-edoobox-canonicalType":"integer","description":"PHP intval-Normalisierung."},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true},"id":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Ortsbenachrichtigung aktualisieren","value":{"data":{"run_hour":9},"id":"placenotification_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"updatePlace","line":180,"branch":{"file":"application/models/v2/Place.php","line":187}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/place/list/workload Belegung für Veranstaltungsort abrufen Liefert eine Tabelle der Termine, die den gewünschten Zeitraum überlappen. filter benötigt start und end als parsebare Datumswerte. Auf der Listenroute muss der erste Filter property=id mit einer einzelnen öffentlichen place-ID sein; mehrere Orte/Räume in einer Abfrage werden nicht unterstützt. Gelöschte bzw. im Papierkorb befindliche Angebote werden ausgeschlossen. Die Zeilen entsprechen der Reihenfolge in columns. - Operation-ID: get_v2_place_list_workload - Dokumentation: /docs/api/v2/operations/get-v2-place-list-workload-82faf9d862ba/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Pflichtfilter: zuerst id, danach start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Pflichtfilter: zuerst id, danach start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"place_012345abcdef_123456"},{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/place/list/workload' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/list/workload"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/list/workload" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_place_list_workload","summary":"Belegung für Veranstaltungsort abrufen","description":"Liefert eine Tabelle der Termine, die den gewünschten Zeitraum überlappen. filter benötigt start und end als parsebare Datumswerte. Auf der Listenroute muss der erste Filter property=id mit einer einzelnen öffentlichen place-ID sein; mehrere Orte/Räume in einer Abfrage werden nicht unterstützt. Gelöschte bzw. im Papierkorb befindliche Angebote werden ausgeschlossen. Die Zeilen entsprechen der Reihenfolge in columns.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Pflichtfilter: zuerst id, danach start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"place_012345abcdef_123456"},{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"offername","type":"string"},{"field":"name","type":"string"},{"field":"offer_id","type":"string"},{"field":"date_id","type":"string"},{"field":"offerdef_id","type":"string"},{"field":"place_id","type":"string"},{"field":"room_id","type":"string"},{"field":"status","type":"integer"},{"field":"date_start","type":"date"},{"field":"date_end","type":"date"}],"rows":[["Beispielangebot","Einführung","offer_012345abcdef_123456","date_012345abcdef_123456","offerdef_012345abcdef_123456","place_012345abcdef_123456","room_012345abcdef_123456",1,"2026-11-03T09:00:00+01:00","2026-11-03T10:00:00+01:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},{"type":"string","pattern":"^offerdef_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerdef_012345abcdef_123456"]},{"anyOf":[{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},{"const":false}]},{"anyOf":[{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},{"const":false}]},{"type":"integer"},{"anyOf":[{"type":"string"},{"const":false}]},{"anyOf":[{"type":"string"},{"const":false}]}],"minItems":10,"maxItems":10}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminfunc.php","line":172},"reason":"Workload SELECT and formatting only; Placefunc194/Roomfunc12 likewise."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"getPlace","line":225,"branch":{"file":"application/models/v2/Place.php","line":238}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/place/notification Benachrichtigungsregel für einen Ort anlegen Legt eine Benachrichtigungsregel zu einem kontoeigenen Veranstaltungsort an. data enthält eine Regel oder eine Liste von Regeln. Pflichtfelder gelten pro Regel; die angegebene emailtemplate muss zur zulässigen E-Mail-Vorlagenfamilie gehören. E-Mail-Adressen werden normalisiert. - Operation-ID: post_v2_place_notification - Dokumentation: /docs/api/v2/operations/post-v2-place-notification-455fa6ba12f7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "place": "place_012345abcdef_123456", "name": "Raumbelegung", "email": [ "planung@example.invalid" ], "rule_type": "days_before", "run_hour": 8, "rule_days_before": 2 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/place/notification' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/place/notification"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "place": "place_012345abcdef_123456", "name": "Raumbelegung", "email": [ "planung@example.invalid" ], "rule_type": "days_before", "run_hour": 8, "rule_days_before": 2 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/place/notification" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"place\":\"place_012345abcdef_123456\",\"name\":\"Raumbelegung\",\"email\":[\"planung@example.invalid\"],\"rule_type\":\"days_before\",\"run_hour\":8,\"rule_days_before\":2}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_place_notification","summary":"Benachrichtigungsregel für einen Ort anlegen","description":"Legt eine Benachrichtigungsregel zu einem kontoeigenen Veranstaltungsort an. data enthält eine Regel oder eine Liste von Regeln. Pflichtfelder gelten pro Regel; die angegebene emailtemplate muss zur zulässigen E-Mail-Vorlagenfamilie gehören. E-Mail-Adressen werden normalisiert.","tags":["place"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"placenotification_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^placenotification_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["placenotification_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"place":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"E-Mail-Liste oder kommagetrennte Zeichenfolge; Leerzeichen und ungültige Adressen werden entfernt."},"rule_type":{"description":"weekday oder days_before; andere Werte werden zu days_before normalisiert.","examples":["days_before","weekday"],"x-edoobox-canonicalType":"string"},"run_hour":{"x-edoobox-canonicalType":"integer","description":"PHP intval-Normalisierung."},"run_weekday":{"x-edoobox-canonicalType":"integer","description":"PHP intval; Werte außerhalb 0 bis 6 werden auf 0 normalisiert."},"rule_days_before":{"x-edoobox-canonicalType":"integer","description":"PHP intval-Normalisierung."},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["place","name","email","rule_type","run_hour"]},{"type":"array","items":{"type":"object","properties":{"place":{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},"name":{"type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"E-Mail-Liste oder kommagetrennte Zeichenfolge; Leerzeichen und ungültige Adressen werden entfernt."},"rule_type":{"description":"weekday oder days_before; andere Werte werden zu days_before normalisiert.","examples":["days_before","weekday"],"x-edoobox-canonicalType":"string"},"run_hour":{"x-edoobox-canonicalType":"integer","description":"PHP intval-Normalisierung."},"run_weekday":{"x-edoobox-canonicalType":"integer","description":"PHP intval; Werte außerhalb 0 bis 6 werden auf 0 normalisiert."},"rule_days_before":{"x-edoobox-canonicalType":"integer","description":"PHP intval-Normalisierung."},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["place","name","email","rule_type","run_hour"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Benachrichtigungsregel für einen Ort anlegen","value":{"data":{"place":"place_012345abcdef_123456","name":"Raumbelegung","email":["planung@example.invalid"],"rule_type":"days_before","run_hour":8,"rule_days_before":2}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Place.php","method":"postPlace","line":157,"branch":{"file":"application/models/v2/Place.php","line":163}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"place","short":"plac","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/plugin Plugin-Katalog und Vertragsbedingungen lesen Liest Katalogangebot, Vertragsstand, Testphase, Fähigkeiten und aktuelle activation.quote. Ein id-Filter bzw. Pfadschlüssel wählt ein Plugin; ohne Selektor wird die Katalogliste geliefert. Die aktuelle Quote wird für eine spätere Aktivierung benötigt. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt. - Operation-ID: get_v2_plugin - Dokumentation: /docs/api/v2/operations/get-v2-plugin-891f6b572ec6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | query | nein | Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"memberships"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/plugin' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/plugin"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/plugin" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_plugin","summary":"Plugin-Katalog und Vertragsbedingungen lesen","description":"Liest Katalogangebot, Vertragsstand, Testphase, Fähigkeiten und aktuelle activation.quote. Ein id-Filter bzw. Pfadschlüssel wählt ein Plugin; ohne Selektor wird die Katalogliste geliefert. Die aktuelle Quote wird für eine spätere Aktivierung benötigt. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt.","tags":["plugin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"memberships"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID."}],"responses":{"200":{"description":"Ein Katalogobjekt beim Selektor, sonst Array. preflight wird nur beim Einzelobjekt ergänzt.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"memberships","key":"memberships","status":"inactive","trial":{"days":14,"available":true,"active":false,"used":false,"started_at":0,"ends_at":0,"ended_at":0},"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"activation":{"resumes_subscription":false,"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1,"trial_days":14},"quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"manufacturer":"edoobox","image_url":"","name_text_code":"N12345","description_text_code":"N12346","feature_text_codes":[],"capabilities":{"plugin_key":"memberships","status":"inactive","version":0,"isVisible":false,"canConfigure":false,"canPurchase":false,"canRedeem":false,"canRunRenewal":false},"catalog_capabilities":{},"subscription":{"billing":null,"current_period_start":0,"current_period_end":0,"next_billing_at":0,"cancel_requested_at":0,"cancel_at_period_end":false,"deactivate_at":0,"setup_billed_at":0},"actions":{"activate":true,"deactivate":false,"cancel_deactivation":false}}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Plugin.php","line":17},"reason":"getPlugin calls catalog and optional preflight. Complete reviewed call graph consists of tenant SELECT/counts, in-memory capability cache and pure billing/trial normalization. activationQuote computes an HMAC consent quote and does not persist it; catalogCurrency fallback only SELECTs. Activation/deactivation and their writes are separate PUT branches.","relatedSources":[{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":21},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":422},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":674},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":758},{"file":"application/libraries/edoobox/Membership/AccountPluginGate.php","line":17},{"file":"application/libraries/edoobox/Membership/PluginTrialPolicy.php","line":46}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Plugin.php","method":"getPlugin","line":15},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"plugin","short":"plug","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/plugin/{id}/activate Plugin aktivieren und Angebot bestätigen Aktiviert das Plugin oder nimmt eine vorgemerkte Kündigung zurück. activation_quote muss unverändert aus activation.quote des aktuellen Katalogs stammen; ein geändertes Angebot wird abgelehnt. Bereits aktive Plugins bleiben unverändert. Eine verfügbare Testphase kann automatisch starten. Aktivierung kann Abrechnung, Audit, Ereignisse und bei memberships eine Katalogvorlage erzeugen. - Operation-ID: put_v2_plugin_id_activate - Dokumentation: /docs/api/v2/operations/put-v2-plugin-id-activate-a29158928f59/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Plugin-Katalogschlüssel. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID.","x-edoobox-id-kind":"plugin-key"},"description":"Plugin-Katalogschlüssel."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "activation_quote": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "trial": false, "request_id": "plugin-example-20261022" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/plugin/'"${PATH_ID}"'/activate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/plugin/{id}/activate"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "activation_quote": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "trial": false, "request_id": "plugin-example-20261022" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/plugin/{id}/activate" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"activation_quote\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"trial\":false,\"request_id\":\"plugin-example-20261022\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_plugin_id_activate","summary":"Plugin aktivieren und Angebot bestätigen","description":"Aktiviert das Plugin oder nimmt eine vorgemerkte Kündigung zurück. activation_quote muss unverändert aus activation.quote des aktuellen Katalogs stammen; ein geändertes Angebot wird abgelehnt. Bereits aktive Plugins bleiben unverändert. Eine verfügbare Testphase kann automatisch starten. Aktivierung kann Abrechnung, Audit, Ereignisse und bei memberships eine Katalogvorlage erzeugen.","tags":["plugin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID.","x-edoobox-id-kind":"plugin-key"},"description":"Plugin-Katalogschlüssel."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Aktualisierter Katalogeintrag mit preflight; status kann deactivation_pending sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"memberships","key":"memberships","status":"active","trial":{"days":14,"available":false,"active":true,"used":true,"started_at":1792627200,"ends_at":1793836800,"ended_at":0},"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"activation":{"resumes_subscription":false,"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1,"trial_days":0},"quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"manufacturer":"edoobox","image_url":"","name_text_code":"N12345","description_text_code":"N12346","feature_text_codes":[],"capabilities":{"plugin_key":"memberships","status":"active","version":1,"isVisible":true,"canConfigure":true,"canPurchase":true,"canRedeem":true,"canRunRenewal":true},"catalog_capabilities":{},"subscription":{"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"current_period_start":1792627200,"current_period_end":1793836800,"next_billing_at":1793836800,"cancel_requested_at":0,"cancel_at_period_end":false,"deactivate_at":0,"setup_billed_at":0},"actions":{"activate":false,"deactivate":true,"cancel_deactivation":false},"preflight":{"published_plans":0,"blocking_memberships":0,"usage_passes_without_end":0,"pending_payments":0,"open_payment_periods":0,"active_auto_renewals":0,"active_pricecategory_conditions":0,"last_valid_until":0,"can_deactivate_now":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"activation_quote":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Für eine zustandsändernde Aktivierung erforderlich; beim bereits aktiven No-op nicht geprüft."},"trial":{"description":"Nur der JSON-Wert true fordert explizit einen Test an; andere Werte werden false. Eine verfügbare Testphase kann unabhängig davon automatisch beginnen.","x-edoobox-canonicalType":"boolean"},"request_id":{"type":"string"}},"additionalProperties":true},"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Plugin aktivieren und Angebot bestätigen","value":{"data":{"activation_quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","trial":false,"request_id":"plugin-example-20261022"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Plugin.php","line":58,"reason":"Plugin::resourceIdentity107–124 consumes the symbolic public plugin_key with this exact regex; PluginCatalogService::catalog64–65 exposes that same key as public id/key.","relatedSources":[{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":64,"reason":"Public id/key are the plugin_key, not an internal edoobox_plugin_id."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Plugin.php","method":"updatePlugin","line":46,"branch":{"file":"application/models/v2/Plugin.php","line":58}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"plugin","short":"plug","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/plugin/{id}/deactivate Plugin kündigen oder deaktivieren Beendet das Plugin sofort oder merkt die Deaktivierung nach laufenden Verträgen/Mitgliedschaften vor. schedule=true erlaubt die spätere Deaktivierung; monatliche Verträge werden bis zum Periodenende vorgemerkt. Aktive Preisbedingungen können blockieren. Vorgemerkte Kündigung bzw. Testkündigung deaktiviert automatische Mitgliedschaftsverlängerungen. reason und request_id sind optionale Auditangaben. - Operation-ID: put_v2_plugin_id_deactivate - Dokumentation: /docs/api/v2/operations/put-v2-plugin-id-deactivate-396fda63c311/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Plugin-Katalogschlüssel. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID.","x-edoobox-id-kind":"plugin-key"},"description":"Plugin-Katalogschlüssel."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "schedule": true, "reason": "Zum Periodenende beenden" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/plugin/'"${PATH_ID}"'/deactivate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/plugin/{id}/deactivate"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "schedule": true, "reason": "Zum Periodenende beenden" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/plugin/{id}/deactivate" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"schedule\":true,\"reason\":\"Zum Periodenende beenden\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_plugin_id_deactivate","summary":"Plugin kündigen oder deaktivieren","description":"Beendet das Plugin sofort oder merkt die Deaktivierung nach laufenden Verträgen/Mitgliedschaften vor. schedule=true erlaubt die spätere Deaktivierung; monatliche Verträge werden bis zum Periodenende vorgemerkt. Aktive Preisbedingungen können blockieren. Vorgemerkte Kündigung bzw. Testkündigung deaktiviert automatische Mitgliedschaftsverlängerungen. reason und request_id sind optionale Auditangaben.","tags":["plugin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID.","x-edoobox-id-kind":"plugin-key"},"description":"Plugin-Katalogschlüssel."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Aktualisierter Katalogeintrag mit preflight; status kann deactivation_pending sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"memberships","key":"memberships","status":"inactive","trial":{"days":14,"available":true,"active":false,"used":false,"started_at":0,"ends_at":0,"ended_at":0},"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"activation":{"resumes_subscription":false,"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1,"trial_days":14},"quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"manufacturer":"edoobox","image_url":"","name_text_code":"N12345","description_text_code":"N12346","feature_text_codes":[],"capabilities":{"plugin_key":"memberships","status":"inactive","version":0,"isVisible":false,"canConfigure":false,"canPurchase":false,"canRedeem":false,"canRunRenewal":false},"catalog_capabilities":{},"subscription":{"billing":null,"current_period_start":0,"current_period_end":0,"next_billing_at":0,"cancel_requested_at":0,"cancel_at_period_end":false,"deactivate_at":0,"setup_billed_at":0},"actions":{"activate":true,"deactivate":false,"cancel_deactivation":false},"preflight":{"published_plans":0,"blocking_memberships":0,"usage_passes_without_end":0,"pending_payments":0,"open_payment_periods":0,"active_auto_renewals":0,"active_pricecategory_conditions":0,"last_valid_until":0,"can_deactivate_now":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"schedule":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"reason":{"type":"string"},"request_id":{"type":"string"}},"additionalProperties":true},"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."}},"additionalProperties":true},"examples":{"request":{"summary":"Plugin kündigen oder deaktivieren","value":{"data":{"schedule":true,"reason":"Zum Periodenende beenden"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Plugin.php","line":79,"reason":"Plugin::resourceIdentity107–124 consumes the symbolic public plugin_key with this exact regex; PluginCatalogService::catalog64–65 exposes that same key as public id/key.","relatedSources":[{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":64,"reason":"Public id/key are the plugin_key, not an internal edoobox_plugin_id."}]},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Plugin.php","method":"updatePlugin","line":46,"branch":{"file":"application/models/v2/Plugin.php","line":79}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"plugin","short":"plug","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/plugin/{id}/preflight Plugin-Deaktivierung prüfen Liest Katalogangebot, Vertragsstand, Testphase, Fähigkeiten und aktuelle activation.quote. Ein id-Filter bzw. Pfadschlüssel wählt ein Plugin; ohne Selektor wird die Katalogliste geliefert. preflight ergänzt beim gewählten Plugin die blockierenden Mitgliedschaften, Zahlungsperioden, Verlängerungen und Preisbedingungen. Ohne Selektor liefert auch diese Route nur die Liste. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt. - Operation-ID: get_v2_plugin_id_preflight - Dokumentation: /docs/api/v2/operations/get-v2-plugin-id-preflight-afcc98d7e886/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Katalogschlüssel, zum Beispiel memberships. | | id | query | nein | Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"memberships"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID.","x-edoobox-id-kind":"plugin-key"},"description":"Katalogschlüssel, zum Beispiel memberships."},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/plugin/'"${PATH_ID}"'/preflight' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/plugin/{id}/preflight"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/plugin/{id}/preflight" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_plugin_id_preflight","summary":"Plugin-Deaktivierung prüfen","description":"Liest Katalogangebot, Vertragsstand, Testphase, Fähigkeiten und aktuelle activation.quote. Ein id-Filter bzw. Pfadschlüssel wählt ein Plugin; ohne Selektor wird die Katalogliste geliefert. preflight ergänzt beim gewählten Plugin die blockierenden Mitgliedschaften, Zahlungsperioden, Verlängerungen und Preisbedingungen. Ohne Selektor liefert auch diese Route nur die Liste. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt.","tags":["plugin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"memberships"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID.","x-edoobox-id-kind":"plugin-key"},"description":"Katalogschlüssel, zum Beispiel memberships."},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID."}],"responses":{"200":{"description":"Ein Katalogobjekt beim Selektor, sonst Array. preflight wird nur beim Einzelobjekt ergänzt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"memberships","key":"memberships","status":"inactive","trial":{"days":14,"available":true,"active":false,"used":false,"started_at":0,"ends_at":0,"ended_at":0},"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"activation":{"resumes_subscription":false,"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1,"trial_days":14},"quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"manufacturer":"edoobox","image_url":"","name_text_code":"N12345","description_text_code":"N12346","feature_text_codes":[],"capabilities":{"plugin_key":"memberships","status":"inactive","version":0,"isVisible":false,"canConfigure":false,"canPurchase":false,"canRedeem":false,"canRunRenewal":false},"catalog_capabilities":{},"subscription":{"billing":null,"current_period_start":0,"current_period_end":0,"next_billing_at":0,"cancel_requested_at":0,"cancel_at_period_end":false,"deactivate_at":0,"setup_billed_at":0},"actions":{"activate":true,"deactivate":false,"cancel_deactivation":false},"preflight":{"published_plans":0,"blocking_memberships":0,"usage_passes_without_end":0,"pending_payments":0,"open_payment_periods":0,"active_auto_renewals":0,"active_pricecategory_conditions":0,"last_valid_until":0,"can_deactivate_now":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Plugin.php","line":36,"reason":"Plugin::resourceIdentity107–124 consumes the symbolic public plugin_key with this exact regex; PluginCatalogService::catalog64–65 exposes that same key as public id/key.","relatedSources":[{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":64,"reason":"Public id/key are the plugin_key, not an internal edoobox_plugin_id."}]},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Plugin.php","line":17},"reason":"getPlugin calls catalog and optional preflight. Complete reviewed call graph consists of tenant SELECT/counts, in-memory capability cache and pure billing/trial normalization. activationQuote computes an HMAC consent quote and does not persist it; catalogCurrency fallback only SELECTs. Activation/deactivation and their writes are separate PUT branches.","relatedSources":[{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":21},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":422},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":674},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":758},{"file":"application/libraries/edoobox/Membership/AccountPluginGate.php","line":17},{"file":"application/libraries/edoobox/Membership/PluginTrialPolicy.php","line":46}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Plugin.php","method":"getPlugin","line":17,"branch":{"file":"application/models/v2/Plugin.php","line":36}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"plugin","short":"plug","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/plugin/list/activate Plugin aktivieren und Angebot bestätigen Aktiviert das Plugin oder nimmt eine vorgemerkte Kündigung zurück. activation_quote muss unverändert aus activation.quote des aktuellen Katalogs stammen; ein geändertes Angebot wird abgelehnt. Bereits aktive Plugins bleiben unverändert. Eine verfügbare Testphase kann automatisch starten. Aktivierung kann Abrechnung, Audit, Ereignisse und bei memberships eine Katalogvorlage erzeugen. - Operation-ID: put_v2_plugin_list_activate - Dokumentation: /docs/api/v2/operations/put-v2-plugin-list-activate-6f00e9a3279e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "activation_quote": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "trial": false, "request_id": "plugin-example-20261022" }, "id": "memberships" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/plugin/list/activate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/plugin/list/activate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "activation_quote": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "trial": false, "request_id": "plugin-example-20261022" }, "id": "memberships" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/plugin/list/activate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"activation_quote\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"trial\":false,\"request_id\":\"plugin-example-20261022\"},\"id\":\"memberships\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_plugin_list_activate","summary":"Plugin aktivieren und Angebot bestätigen","description":"Aktiviert das Plugin oder nimmt eine vorgemerkte Kündigung zurück. activation_quote muss unverändert aus activation.quote des aktuellen Katalogs stammen; ein geändertes Angebot wird abgelehnt. Bereits aktive Plugins bleiben unverändert. Eine verfügbare Testphase kann automatisch starten. Aktivierung kann Abrechnung, Audit, Ereignisse und bei memberships eine Katalogvorlage erzeugen.","tags":["plugin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Aktualisierter Katalogeintrag mit preflight; status kann deactivation_pending sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"memberships","key":"memberships","status":"active","trial":{"days":14,"available":false,"active":true,"used":true,"started_at":1792627200,"ends_at":1793836800,"ended_at":0},"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"activation":{"resumes_subscription":false,"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1,"trial_days":0},"quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"manufacturer":"edoobox","image_url":"","name_text_code":"N12345","description_text_code":"N12346","feature_text_codes":[],"capabilities":{"plugin_key":"memberships","status":"active","version":1,"isVisible":true,"canConfigure":true,"canPurchase":true,"canRedeem":true,"canRunRenewal":true},"catalog_capabilities":{},"subscription":{"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"current_period_start":1792627200,"current_period_end":1793836800,"next_billing_at":1793836800,"cancel_requested_at":0,"cancel_at_period_end":false,"deactivate_at":0,"setup_billed_at":0},"actions":{"activate":false,"deactivate":true,"cancel_deactivation":false},"preflight":{"published_plans":0,"blocking_memberships":0,"usage_passes_without_end":0,"pending_payments":0,"open_payment_periods":0,"active_auto_renewals":0,"active_pricecategory_conditions":0,"last_valid_until":0,"can_deactivate_now":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"activation_quote":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Für eine zustandsändernde Aktivierung erforderlich; beim bereits aktiven No-op nicht geprüft."},"trial":{"description":"Nur der JSON-Wert true fordert explizit einen Test an; andere Werte werden false. Eine verfügbare Testphase kann unabhängig davon automatisch beginnen.","x-edoobox-canonicalType":"boolean"},"request_id":{"type":"string"}},"additionalProperties":true},"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Plugin aktivieren und Angebot bestätigen","value":{"data":{"activation_quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","trial":false,"request_id":"plugin-example-20261022"},"id":"memberships"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Plugin.php","method":"updatePlugin","line":46,"branch":{"file":"application/models/v2/Plugin.php","line":58}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"plugin","short":"plug","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/plugin/list/deactivate Plugin kündigen oder deaktivieren Beendet das Plugin sofort oder merkt die Deaktivierung nach laufenden Verträgen/Mitgliedschaften vor. schedule=true erlaubt die spätere Deaktivierung; monatliche Verträge werden bis zum Periodenende vorgemerkt. Aktive Preisbedingungen können blockieren. Vorgemerkte Kündigung bzw. Testkündigung deaktiviert automatische Mitgliedschaftsverlängerungen. reason und request_id sind optionale Auditangaben. - Operation-ID: put_v2_plugin_list_deactivate - Dokumentation: /docs/api/v2/operations/put-v2-plugin-list-deactivate-abb9a71b2f6e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "schedule": true, "reason": "Zum Periodenende beenden" }, "id": "memberships" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/plugin/list/deactivate' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/plugin/list/deactivate"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "schedule": true, "reason": "Zum Periodenende beenden" }, "id": "memberships" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/plugin/list/deactivate" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"schedule\":true,\"reason\":\"Zum Periodenende beenden\"},\"id\":\"memberships\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_plugin_list_deactivate","summary":"Plugin kündigen oder deaktivieren","description":"Beendet das Plugin sofort oder merkt die Deaktivierung nach laufenden Verträgen/Mitgliedschaften vor. schedule=true erlaubt die spätere Deaktivierung; monatliche Verträge werden bis zum Periodenende vorgemerkt. Aktive Preisbedingungen können blockieren. Vorgemerkte Kündigung bzw. Testkündigung deaktiviert automatische Mitgliedschaftsverlängerungen. reason und request_id sind optionale Auditangaben.","tags":["plugin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Aktualisierter Katalogeintrag mit preflight; status kann deactivation_pending sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"memberships","key":"memberships","status":"inactive","trial":{"days":14,"available":true,"active":false,"used":false,"started_at":0,"ends_at":0,"ended_at":0},"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"activation":{"resumes_subscription":false,"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1,"trial_days":14},"quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"manufacturer":"edoobox","image_url":"","name_text_code":"N12345","description_text_code":"N12346","feature_text_codes":[],"capabilities":{"plugin_key":"memberships","status":"inactive","version":0,"isVisible":false,"canConfigure":false,"canPurchase":false,"canRedeem":false,"canRunRenewal":false},"catalog_capabilities":{},"subscription":{"billing":null,"current_period_start":0,"current_period_end":0,"next_billing_at":0,"cancel_requested_at":0,"cancel_at_period_end":false,"deactivate_at":0,"setup_billed_at":0},"actions":{"activate":true,"deactivate":false,"cancel_deactivation":false},"preflight":{"published_plans":0,"blocking_memberships":0,"usage_passes_without_end":0,"pending_payments":0,"open_payment_periods":0,"active_auto_renewals":0,"active_pricecategory_conditions":0,"last_valid_until":0,"can_deactivate_now":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"schedule":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"reason":{"type":"string"},"request_id":{"type":"string"}},"additionalProperties":true},"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Plugin kündigen oder deaktivieren","value":{"data":{"schedule":true,"reason":"Zum Periodenende beenden"},"id":"memberships"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Plugin.php","method":"updatePlugin","line":46,"branch":{"file":"application/models/v2/Plugin.php","line":79}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"plugin","short":"plug","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/plugin/list/preflight Plugin-Deaktivierung prüfen Liest Katalogangebot, Vertragsstand, Testphase, Fähigkeiten und aktuelle activation.quote. Ein id-Filter bzw. Pfadschlüssel wählt ein Plugin; ohne Selektor wird die Katalogliste geliefert. preflight ergänzt beim gewählten Plugin die blockierenden Mitgliedschaften, Zahlungsperioden, Verlängerungen und Preisbedingungen. Ohne Selektor liefert auch diese Route nur die Liste. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt. - Operation-ID: get_v2_plugin_list_preflight - Dokumentation: /docs/api/v2/operations/get-v2-plugin-list-preflight-389b761979df/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | query | nein | Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"memberships"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/plugin/list/preflight' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/plugin/list/preflight"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/plugin/list/preflight" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_plugin_list_preflight","summary":"Plugin-Deaktivierung prüfen","description":"Liest Katalogangebot, Vertragsstand, Testphase, Fähigkeiten und aktuelle activation.quote. Ein id-Filter bzw. Pfadschlüssel wählt ein Plugin; ohne Selektor wird die Katalogliste geliefert. preflight ergänzt beim gewählten Plugin die blockierenden Mitgliedschaften, Zahlungsperioden, Verlängerungen und Preisbedingungen. Ohne Selektor liefert auch diese Route nur die Liste. Alternativ zum id-Filter wird ein direkter Query-Parameter id unterstützt.","tags":["plugin"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"JSON-kodierte Filterliste. Diese Ressource wertet nur die genannten Eigenschaften aus.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","enum":["id"]},"expression":{"type":"string","description":"Hier wird Gleichheit verwendet; expression wird ignoriert."},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"id","value":"memberships"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"]},"description":"Optionaler direkter Selektor; hat vor einem id-Filter Vorrang. Plugin-Katalogschlüssel, keine kodierte DB-ID."}],"responses":{"200":{"description":"Ein Katalogobjekt beim Selektor, sonst Array. preflight wird nur beim Einzelobjekt ergänzt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"memberships","key":"memberships","status":"inactive","trial":{"days":14,"available":true,"active":false,"used":false,"started_at":0,"ends_at":0,"ended_at":0},"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1},"activation":{"resumes_subscription":false,"billing":{"model":"monthly","free":false,"amount":"20.00","currency":"CHF","setup_amount":"0.00","setup_free":true,"minimum_term_months":1,"trial_days":14},"quote":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},"manufacturer":"edoobox","image_url":"","name_text_code":"N12345","description_text_code":"N12346","feature_text_codes":[],"capabilities":{"plugin_key":"memberships","status":"inactive","version":0,"isVisible":false,"canConfigure":false,"canPurchase":false,"canRedeem":false,"canRunRenewal":false},"catalog_capabilities":{},"subscription":{"billing":null,"current_period_start":0,"current_period_end":0,"next_billing_at":0,"cancel_requested_at":0,"cancel_at_period_end":false,"deactivate_at":0,"setup_billed_at":0},"actions":{"activate":true,"deactivate":false,"cancel_deactivation":false},"preflight":{"published_plans":0,"blocking_memberships":0,"usage_passes_without_end":0,"pending_payments":0,"open_payment_periods":0,"active_auto_renewals":0,"active_pricecategory_conditions":0,"last_valid_until":0,"can_deactivate_now":true}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"key":{"type":"string","pattern":"^[a-z][a-z0-9_-]{1,63}$","examples":["memberships"],"description":"Plugin-Katalogschlüssel, keine kodierte DB-ID."},"status":{"type":"string"},"trial":{"type":"object","properties":{"days":{"type":"integer"},"started_at":{"type":"integer"},"ends_at":{"type":"integer"},"ended_at":{"type":"integer"},"available":{"type":"boolean"},"active":{"type":"boolean"},"used":{"type":"boolean"}},"additionalProperties":true},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"activation":{"type":"object","properties":{"resumes_subscription":{"type":"boolean"},"billing":{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},"quote":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true},"manufacturer":{"type":"string"},"image_url":{"type":"string"},"name_text_code":{"type":"string"},"description_text_code":{"type":"string"},"feature_text_codes":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"plugin_key":{"type":"string"},"status":{"type":"string"},"version":{"type":"integer"},"isVisible":{"type":"boolean"},"canConfigure":{"type":"boolean"},"canPurchase":{"type":"boolean"},"canRedeem":{"type":"boolean"},"canRunRenewal":{"type":"boolean"}},"additionalProperties":true},"catalog_capabilities":{"type":"object","properties":{},"additionalProperties":true},"subscription":{"type":"object","properties":{"billing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string"},"free":{"type":"boolean"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"setup_amount":{"type":"string"},"setup_free":{"type":"boolean"},"minimum_term_months":{"type":"integer"},"trial_days":{"type":"integer"}},"additionalProperties":true},{"type":"null"}]},"current_period_start":{"type":"integer"},"current_period_end":{"type":"integer"},"next_billing_at":{"type":"integer"},"cancel_requested_at":{"type":"integer"},"deactivate_at":{"type":"integer"},"setup_billed_at":{"type":"integer"},"cancel_at_period_end":{"type":"boolean"}},"additionalProperties":true},"actions":{"type":"object","properties":{"activate":{"type":"boolean"},"deactivate":{"type":"boolean"},"cancel_deactivation":{"type":"boolean"}},"additionalProperties":true},"preflight":{"type":"object","properties":{"published_plans":{"type":"integer"},"blocking_memberships":{"type":"integer"},"usage_passes_without_end":{"type":"integer"},"pending_payments":{"type":"integer"},"open_payment_periods":{"type":"integer"},"active_auto_renewals":{"type":"integer"},"active_pricecategory_conditions":{"type":"integer"},"last_valid_until":{"type":"integer"},"can_deactivate_now":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Plugin.php","line":17},"reason":"getPlugin calls catalog and optional preflight. Complete reviewed call graph consists of tenant SELECT/counts, in-memory capability cache and pure billing/trial normalization. activationQuote computes an HMAC consent quote and does not persist it; catalogCurrency fallback only SELECTs. Activation/deactivation and their writes are separate PUT branches.","relatedSources":[{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":21},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":422},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":674},{"file":"application/libraries/edoobox/Membership/PluginCatalogService.php","line":758},{"file":"application/libraries/edoobox/Membership/AccountPluginGate.php","line":17},{"file":"application/libraries/edoobox/Membership/PluginTrialPolicy.php","line":46}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Plugin.php","method":"getPlugin","line":17,"branch":{"file":"application/models/v2/Plugin.php","line":36}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"plugin","short":"plug","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/pricecategory Preiskategorien auflisten Liest Preiskategorien im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. membership_conditions wird nur bei passender Plugin-/Schemafreigabe und Nicht-Standardpreiskategorien ergänzt; das Feld kann deshalb fehlen. - Operation-ID: get_v2_pricecategory - Dokumentation: /docs/api/v2/operations/get-v2-pricecategory-28a65409e2b0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/pricecategory' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pricecategory"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pricecategory" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_pricecategory","summary":"Preiskategorien auflisten","description":"Liest Preiskategorien im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. membership_conditions wird nur bei passender Plugin-/Schemafreigabe und Nicht-Standardpreiskategorien ergänzt; das Feld kann deshalb fehlen.","tags":["pricecategory"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"pricecategory_012345abcdef_123456":{"id":"pricecategory_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Pricecategory.php","method":"getPricecategory","line":267},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"pricecategory","short":"prca","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/pricecategory Preiskategorie anlegen Legt Preiskategorie mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. offer ist beim Anlegen erforderlich und danach unveränderlich. Mitgliedschaftsbedingungen unterliegen der Pluginfreigabe und den Regeln im Feldschema; neue Kategorien unterliegen dem Abo-Limit. - Operation-ID: post_v2_pricecategory - Dokumentation: /docs/api/v2/operations/post-v2-pricecategory-01750cbd9ee4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/pricecategory' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pricecategory"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "name": "Example" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pricecategory" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_pricecategory","summary":"Preiskategorie anlegen","description":"Legt Preiskategorie mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. offer ist beim Anlegen erforderlich und danach unveränderlich. Mitgliedschaftsbedingungen unterliegen der Pluginfreigabe und den Regeln im Feldschema; neue Kategorien unterliegen dem Abo-Limit.","tags":["pricecategory"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"pricecategory_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["pricecategory_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["pricecategory_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"offer":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":52},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":79,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"description":"Client default value ignored; getter always writes 0. Single POST supplies absent field as 0; batch absence follows DB default.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-ignoredInput":true,"examples":[true,{"ignored":"synthetic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":104,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":113,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":122},"date_end_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":140},"customer_limit":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":149,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"amount":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":158,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"membership_conditions":{"anyOf":[{"type":"null"},{"const":false},{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["id"]},{"type":"object","properties":{"membership_plan":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["membership_plan"]}]}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1},{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["id"]},{"type":"object","properties":{"membership_plan":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["membership_plan"]}]}}},"additionalProperties":true,"required":["type","membership_plans"]}],"description":"Public symmetric list is [] or one membership_any_of record. The legacy single-object shape remains accepted; null/false clears conditions. Membership plans must be valid public IDs and have eligible state.","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["offer","name"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"offer":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":52},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":79,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"description":"Client default value ignored; getter always writes 0. Single POST supplies absent field as 0; batch absence follows DB default.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-ignoredInput":true,"examples":[true,{"ignored":"synthetic"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":104,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":113,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":122},"date_end_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":140},"customer_limit":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":149,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"amount":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":158,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"membership_conditions":{"anyOf":[{"type":"null"},{"const":false},{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["id"]},{"type":"object","properties":{"membership_plan":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["membership_plan"]}]}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1},{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["id"]},{"type":"object","properties":{"membership_plan":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["membership_plan"]}]}}},"additionalProperties":true,"required":["type","membership_plans"]}],"description":"Public symmetric list is [] or one membership_any_of record. The legacy single-object shape remains accepted; null/false clears conditions. Membership plans must be valid public IDs and have eligible state.","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["offer","name"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"offer":"offer_012345abcdef_123456","name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Pricecategory.php","method":"postPricecategory","line":208},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"pricecategory","short":"prca","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/pricecategory Preiskategorie aktualisieren Ändert die übergebenen schreibbaren Felder in data für Preiskategorie. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. offer ist beim Anlegen erforderlich und danach unveränderlich. Mitgliedschaftsbedingungen unterliegen der Pluginfreigabe und den Regeln im Feldschema; neue Kategorien unterliegen dem Abo-Limit. - Operation-ID: put_v2_pricecategory - Dokumentation: /docs/api/v2/operations/put-v2-pricecategory-0f85fb7354b8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" }, "id": "pricecategory_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/pricecategory' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pricecategory"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" }, "id": "pricecategory_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pricecategory" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"},\"id\":\"pricecategory_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_pricecategory","summary":"Preiskategorie aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Preiskategorie. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. offer ist beim Anlegen erforderlich und danach unveränderlich. Mitgliedschaftsbedingungen unterliegen der Pluginfreigabe und den Regeln im Feldschema; neue Kategorien unterliegen dem Abo-Limit.","tags":["pricecategory"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"pricecategory_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["pricecategory_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"offer":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":52},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":79,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":false,"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":104,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":113,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":122},"date_end_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":140},"customer_limit":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":149,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"amount":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":158,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"membership_conditions":{"anyOf":[{"type":"null"},{"const":false},{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["id"]},{"type":"object","properties":{"membership_plan":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["membership_plan"]}]}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1},{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["id"]},{"type":"object","properties":{"membership_plan":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["membership_plan"]}]}}},"additionalProperties":true,"required":["type","membership_plans"]}],"description":"Public symmetric list is [] or one membership_any_of record. The legacy single-object shape remains accepted; null/false clears conditions. Membership plans must be valid public IDs and have eligible state.","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["offer","default"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Preiskategorie aktualisieren","value":{"data":{"name":"Example"},"id":"pricecategory_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Pricecategory.php","method":"updatePricecategory","line":235},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"pricecategory","short":"prca","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/pricecategory/{id} Preiskategorie abrufen Liest Preiskategorie anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. membership_conditions wird nur bei passender Plugin-/Schemafreigabe und Nicht-Standardpreiskategorien ergänzt; das Feld kann deshalb fehlen. - Operation-ID: get_v2_pricecategory_id - Dokumentation: /docs/api/v2/operations/get-v2-pricecategory-id-7cdab26e08db/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/pricecategory/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pricecategory/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pricecategory/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_pricecategory_id","summary":"Preiskategorie abrufen","description":"Liest Preiskategorie anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. membership_conditions wird nur bei passender Plugin-/Schemafreigabe und Nicht-Standardpreiskategorien ergänzt; das Feld kann deshalb fehlen.","tags":["pricecategory"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"pricecategory_012345abcdef_123456":{"id":"pricecategory_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Pricecategory.php","method":"getPricecategory","line":267},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"pricecategory","short":"prca","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/pricecategory/{id} Preiskategorie aktualisieren Ändert die übergebenen schreibbaren Felder in data für Preiskategorie. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. offer ist beim Anlegen erforderlich und danach unveränderlich. Mitgliedschaftsbedingungen unterliegen der Pluginfreigabe und den Regeln im Feldschema; neue Kategorien unterliegen dem Abo-Limit. - Operation-ID: put_v2_pricecategory_id - Dokumentation: /docs/api/v2/operations/put-v2-pricecategory-id-c23a67fd0482/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/pricecategory/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pricecategory/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pricecategory/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_pricecategory_id","summary":"Preiskategorie aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Preiskategorie. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. offer ist beim Anlegen erforderlich und danach unveränderlich. Mitgliedschaftsbedingungen unterliegen der Pluginfreigabe und den Regeln im Feldschema; neue Kategorien unterliegen dem Abo-Limit.","tags":["pricecategory"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"pricecategory_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["pricecategory_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"offer":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":52},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":79,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":false,"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":104,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"date_start_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":113,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":122},"date_end_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-sourceLine":140},"customer_limit":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":149,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"amount":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":158,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"membership_conditions":{"anyOf":[{"type":"null"},{"const":false},{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["id"]},{"type":"object","properties":{"membership_plan":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["membership_plan"]}]}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1},{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["id"]},{"type":"object","properties":{"membership_plan":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}},"additionalProperties":true,"required":["membership_plan"]}]}}},"additionalProperties":true,"required":["type","membership_plans"]}],"description":"Public symmetric list is [] or one membership_any_of record. The legacy single-object shape remains accepted; null/false clears conditions. Membership plans must be valid public IDs and have eligible state.","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["offer","default"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Pricecategory.php","method":"updatePricecategory","line":235},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"pricecategory","short":"prca","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/pricecategory/{id}/data Belegung und freie Plätze einer Preiskategorie prüfen Liefert zugeordnete Buchungen, noch gültige Reservierungstimer und verfügbare Plätze. Bei Standardkategorien gilt die Angebotskapazität, bei anderen Kategorien zusätzlich customer_limit. Bei freier Terminauswahl wird die Kapazität entsprechend den zukünftigen Terminen berechnet; available ist mindestens 0. Die Operation reserviert keine Plätze. - Operation-ID: get_v2_pricecategory_id_data - Dokumentation: /docs/api/v2/operations/get-v2-pricecategory-id-data-f548299b4e23/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/pricecategory/'"${PATH_ID}"'/data' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pricecategory/{id}/data"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pricecategory/{id}/data" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_pricecategory_id_data","summary":"Belegung und freie Plätze einer Preiskategorie prüfen","description":"Liefert zugeordnete Buchungen, noch gültige Reservierungstimer und verfügbare Plätze. Bei Standardkategorien gilt die Angebotskapazität, bei anderen Kategorien zusätzlich customer_limit. Bei freier Terminauswahl wird die Kapazität entsprechend den zukünftigen Terminen berechnet; available ist mindestens 0. Die Operation reserviert keine Plätze.","tags":["pricecategory"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"pricecategory_012345abcdef_123456","offer":"offer_012345abcdef_123456","statistics":{"assigned":8,"reserved":2,"available":10}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"statistics":{"type":"object","properties":{"assigned":{"type":"integer"},"reserved":{"type":"integer"},"available":{"type":"integer"}},"additionalProperties":true,"required":["assigned","reserved","available"]}},"additionalProperties":true,"required":["id","offer","statistics"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Pricecategory.php","line":313},"reason":"Reviewed complete data branch: constructor only declares Structure/loads API_DATA; getPricecategory selects getPricecategoryData, which decodes the account pricecategory ID, performs SELECT/COUNT queries for active bookings and unexpired reservations, clamps computed availability, and serializes public IDs/statistics. ParticipantBooking::activeBookingSql validates an alias and returns an SQL predicate string. No INSERT/UPDATE/DELETE, mail, provider call, default creation or cleanup is reached.","relatedSources":[{"file":"application/libraries/edoobox/helper/ParticipantBooking.php","line":13}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Pricecategory.php","method":"getPricecategory","line":267,"branch":{"file":"application/models/v2/Pricecategory.php","line":296}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"pricecategory","short":"prca","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/pricecategory/list Preiskategorien auflisten Liest Preiskategorien im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. membership_conditions wird nur bei passender Plugin-/Schemafreigabe und Nicht-Standardpreiskategorien ergänzt; das Feld kann deshalb fehlen. - Operation-ID: get_v2_pricecategory_list - Dokumentation: /docs/api/v2/operations/get-v2-pricecategory-list-42e7685d50ec/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/pricecategory/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pricecategory/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pricecategory/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_pricecategory_list","summary":"Preiskategorien auflisten","description":"Liest Preiskategorien im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. membership_conditions wird nur bei passender Plugin-/Schemafreigabe und Nicht-Standardpreiskategorien ergänzt; das Feld kann deshalb fehlen.","tags":["pricecategory"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"pricecategory_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"pricecategory_012345abcdef_123456":{"id":"pricecategory_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["pricecategory_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":52,"type":"string"},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"boolean"},"default":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategory.php","line":89,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/Pricecategory.php","line":208,"reason":"Exact POST/PUT callers.","sha256":"3d9a3db0bad3f4417c7a25d9787fb19620cc1ffe3e52299dca0f051ec9817eea"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1795,"reason":"update:false skipped before getter/hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1987,"reason":"No effective update yields ED80100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch processes supplied fields only.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"ID insert hook handles membership_conditions, not default.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"order":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":104,"type":"integer"},"date_start_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":113,"type":"boolean"},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":122,"anyOf":[{"type":"string"},{"const":false}]},"date_end_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"boolean"},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"anyOf":[{"type":"string"},{"const":false}]},"customer_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":149,"type":"integer"},"amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":158},"membership_conditions":{"type":"array","items":{"type":"object","properties":{"type":{"const":"membership_any_of"},"membership_plans":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."}}},"additionalProperties":true,"required":["type","membership_plans"]},"maxItems":1,"x-edoobox-contractSource":[{"file":"application/models/v2/Pricecategoryfunc.php","line":8,"reason":"POST id insert callback persists supplied conditions.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Pricecategoryfunc.php","line":31,"reason":"PUT callback persists the same condition payload.","sha256":"4ff29ba28e2856a3e09d586b8ce368a73160137d5395d3e2c7512e2169df85be"},{"file":"application/models/v2/Membershipbookingfunc.php","line":3319,"reason":"Accepts clear sentinels, an empty/one-record list, or the legacy single record; public plan-ID alternatives are normalized.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"},{"file":"application/models/v2/Membershipbookingfunc.php","line":43,"reason":"Response enrichment returns [] or a one-record list of public membershipplan IDs.","sha256":"a70c2cd0c3c0625a378357bd1d275adf29a24fbe3c7e31432033bbbb5e16be94"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Pricecategory.php","method":"getPricecategory","line":267},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"pricecategory","short":"prca","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/promotion Rabatt- und Gutscheinkampagnen abrufen Liest Kampagnen mit Zeitraum, Rabattwert, Mindestbetrag, Buchungsgrenzen und Angebots-/Tagzuordnungen. offer- und category-Filter berücksichtigen den Angebotsbaum. fields possibleUses bzw. amountUses ergänzt Code- und Verwendungszahlen. - Operation-ID: get_v2_promotion - Dokumentation: /docs/api/v2/operations/get-v2-promotion-3593210f4304/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/promotion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotion"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotion" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_promotion","summary":"Rabatt- und Gutscheinkampagnen abrufen","description":"Liest Kampagnen mit Zeitraum, Rabattwert, Mindestbetrag, Buchungsgrenzen und Angebots-/Tagzuordnungen. offer- und category-Filter berücksichtigen den Angebotsbaum. fields possibleUses bzw. amountUses ergänzt Code- und Verwendungszahlen.","tags":["promotion"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"promotion_012345abcdef_123456":{"id":"promotion_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotion.php","method":"getPromotion","line":455},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"promotion","short":"prom","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/promotion Rabatt- oder Gutscheinkampagne anlegen Erstellt eine campaign- oder sale-Kampagne mit Verrechnungsart, Zeitraum, Wert und Währung. validity_range und tags akzeptieren ausschließlich vorhandene öffentliche IDs des Kontos; [] legt keine Zuordnung an. Ein Objekt oder eine generische Liste von Objekten wird unterstützt; die Feldnormalisierung gilt weiterhin. - Operation-ID: post_v2_promotion - Dokumentation: /docs/api/v2/operations/post-v2-promotion-83291003b4be/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Herbstaktion", "status": true, "creation": "campaign", "offsetting": "total", "date_start": "2026-11-01T00:00:00+01:00", "date_end": "2026-11-30T23:59:59+01:00", "booking_maximum": 1, "total_award": 10, "minimum_amount": 50, "currency": "CHF", "code_alias": "HERBST", "settlement": "percent", "validity_range": [ "offer_012345abcdef_123456" ], "tags": [] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/promotion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotion"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Herbstaktion", "status": true, "creation": "campaign", "offsetting": "total", "date_start": "2026-11-01T00:00:00+01:00", "date_end": "2026-11-30T23:59:59+01:00", "booking_maximum": 1, "total_award": 10, "minimum_amount": 50, "currency": "CHF", "code_alias": "HERBST", "settlement": "percent", "validity_range": [ "offer_012345abcdef_123456" ], "tags": [] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotion" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Herbstaktion\",\"status\":true,\"creation\":\"campaign\",\"offsetting\":\"total\",\"date_start\":\"2026-11-01T00:00:00+01:00\",\"date_end\":\"2026-11-30T23:59:59+01:00\",\"booking_maximum\":1,\"total_award\":10,\"minimum_amount\":50,\"currency\":\"CHF\",\"code_alias\":\"HERBST\",\"settlement\":\"percent\",\"validity_range\":[\"offer_012345abcdef_123456\"],\"tags\":[]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_promotion","summary":"Rabatt- oder Gutscheinkampagne anlegen","description":"Erstellt eine campaign- oder sale-Kampagne mit Verrechnungsart, Zeitraum, Wert und Währung. validity_range und tags akzeptieren ausschließlich vorhandene öffentliche IDs des Kontos; [] legt keine Zuordnung an. Ein Objekt oder eine generische Liste von Objekten wird unterstützt; die Feldnormalisierung gilt weiterhin.","tags":["promotion"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Generische Insert-Bestätigung mit insert und öffentlicher ID bzw. mehreren IDs bei Batch-Anlage.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"promotion_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":34},"status":{"x-edoobox-canonicalType":"boolean","description":"PHP value==false ->0 else1. Canonical booleans supported; other values coerce. No strict wire bool validator.\n\nLoose false comparison. insert_default injects input0 when absent, so omitted status defaults inactive despite metadata flagtrue.","x-edoobox-schemaStatus":"source-reviewed","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"x-edoobox-knownInputs":["campaign","sale"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["campaign"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":57},"offsetting":{"x-edoobox-knownInputs":["offer","total"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":81},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":105},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":118},"booking_maximum":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"total_award":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":147,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"minimum_amount":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":163,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"bookingType":{"x-edoobox-knownInputs":["all","normal","offerlist","waitinglist"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["all"],"x-edoobox-sourceLine":179},"allowedDomains":{"description":"Retains strings passing email check or FILTER_VALIDATE_URL(http://+entry). Invalid entries filtered, not rejected; non-array becomes[]. Original keys retained, so sparse result can output object.\n\nNon-array ->[]. Email or prepended-http URL filter; array_filter preserves numeric/string keys, so response may be list or keyed object. Retained members are not cast: PHP filter/concatenation may coerce nonstring members during checks, but original values remain in encoded output.\n\nScope / limitations: URL-like validation is not strict DNS domain validation; no rejecting hostname/uniqueItems rule.","x-edoobox-canonicalType":"array","x-edoobox-canonicalItems":"string","x-edoobox-schemaStatus":"source-reviewed","examples":[["member@example.invalid","example.invalid"]],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces include/exclude tag associations. Requires a JSON list; every typed public tag must exist in the selected account. Unknown modes and any invalid/foreign member reject before relation writes. Duplicate mode/tag pairs are normalized; [] clears.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":3,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":275},"code_alias":{"x-edoobox-canonicalType":"string","description":"Trim/delete chars outside A-Z/a-z/0-9/_/-, then aP(...,true,10). Invalid characters/excess length normalize, not reject.\n\npreg_replace removes disallowed characters; aP truncates resulting escaped string to10.","x-edoobox-schemaStatus":"source-reviewed","examples":["SAVE_30"],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"x-edoobox-knownInputs":["percent","amount"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["percent"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":303},"validity_range":{"type":"array","items":{"type":"string","pattern":"^(?:offer|category)_[a-f0-9]{12}_-?[0-9]+$","description":"Public offer or category v2 ID returned for an existing same-account course or folder/root2 respectively."},"description":"JSON list of account-owned existing offers/categories. Complete typed/resource-subtype validation happens before validity relations change; duplicate targets are normalized and [] clears the range.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","creation","offsetting","date_start","date_end","booking_maximum","total_award","minimum_amount","currency","code_alias","settlement"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":34},"status":{"x-edoobox-canonicalType":"boolean","description":"PHP value==false ->0 else1. Canonical booleans supported; other values coerce. No strict wire bool validator.\n\nLoose false comparison. insert_default injects input0 when absent, so omitted status defaults inactive despite metadata flagtrue.","x-edoobox-schemaStatus":"source-reviewed","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"x-edoobox-knownInputs":["campaign","sale"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["campaign"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":57},"offsetting":{"x-edoobox-knownInputs":["offer","total"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":81},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":105},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":118},"booking_maximum":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"total_award":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":147,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"minimum_amount":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":163,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"bookingType":{"x-edoobox-knownInputs":["all","normal","offerlist","waitinglist"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["all"],"x-edoobox-sourceLine":179},"allowedDomains":{"description":"Retains strings passing email check or FILTER_VALIDATE_URL(http://+entry). Invalid entries filtered, not rejected; non-array becomes[]. Original keys retained, so sparse result can output object.\n\nNon-array ->[]. Email or prepended-http URL filter; array_filter preserves numeric/string keys, so response may be list or keyed object. Retained members are not cast: PHP filter/concatenation may coerce nonstring members during checks, but original values remain in encoded output.\n\nScope / limitations: URL-like validation is not strict DNS domain validation; no rejecting hostname/uniqueItems rule.","x-edoobox-canonicalType":"array","x-edoobox-canonicalItems":"string","x-edoobox-schemaStatus":"source-reviewed","examples":[["member@example.invalid","example.invalid"]],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces include/exclude tag associations. Requires a JSON list; every typed public tag must exist in the selected account. Unknown modes and any invalid/foreign member reject before relation writes. Duplicate mode/tag pairs are normalized; [] clears.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":3,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":275},"code_alias":{"x-edoobox-canonicalType":"string","description":"Trim/delete chars outside A-Z/a-z/0-9/_/-, then aP(...,true,10). Invalid characters/excess length normalize, not reject.\n\npreg_replace removes disallowed characters; aP truncates resulting escaped string to10.","x-edoobox-schemaStatus":"source-reviewed","examples":["SAVE_30"],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"x-edoobox-knownInputs":["percent","amount"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["percent"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":303},"validity_range":{"type":"array","items":{"type":"string","pattern":"^(?:offer|category)_[a-f0-9]{12}_-?[0-9]+$","description":"Public offer or category v2 ID returned for an existing same-account course or folder/root2 respectively."},"description":"JSON list of account-owned existing offers/categories. Complete typed/resource-subtype validation happens before validity relations change; duplicate targets are normalized and [] clears the range.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","creation","offsetting","date_start","date_end","booking_maximum","total_award","minimum_amount","currency","code_alias","settlement"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Rabatt- oder Gutscheinkampagne anlegen","value":{"data":{"name":"Herbstaktion","status":true,"creation":"campaign","offsetting":"total","date_start":"2026-11-01T00:00:00+01:00","date_end":"2026-11-30T23:59:59+01:00","booking_maximum":1,"total_award":10,"minimum_amount":50,"currency":"CHF","code_alias":"HERBST","settlement":"percent","validity_range":["offer_012345abcdef_123456"],"tags":[]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotion.php","method":"postPromotion","line":408},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"promotion","short":"prom","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/promotion Kampagne und Gültigkeitszuordnungen ändern Ändert eine Kampagne. currency bleibt unveränderlich. Übergebene validity_range- und tags-Listen ersetzen jeweils alle Zuordnungen; [] entfernt sie. Die Hooks prüfen alle IDs und Kontozuordnungen vor der Änderung. - Operation-ID: put_v2_promotion - Dokumentation: /docs/api/v2/operations/put-v2-promotion-27edf63f6f17/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false }, "id": "promotion_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/promotion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotion"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false }, "id": "promotion_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotion" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false},\"id\":\"promotion_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_promotion","summary":"Kampagne und Gültigkeitszuordnungen ändern","description":"Ändert eine Kampagne. currency bleibt unveränderlich. Übergebene validity_range- und tags-Listen ersetzen jeweils alle Zuordnungen; [] entfernt sie. Die Hooks prüfen alle IDs und Kontozuordnungen vor der Änderung.","tags":["promotion"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Generische Update-Bestätigung mit update und öffentlicher ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"promotion_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":34},"status":{"x-edoobox-canonicalType":"boolean","description":"PHP value==false ->0 else1. Canonical booleans supported; other values coerce. No strict wire bool validator.\n\nLoose false comparison. insert_default injects input0 when absent, so omitted status defaults inactive despite metadata flagtrue.","x-edoobox-schemaStatus":"source-reviewed","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"creation":{"x-edoobox-knownInputs":["campaign","sale"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["campaign"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":57},"offsetting":{"x-edoobox-knownInputs":["offer","total"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":81},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":105},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":118},"booking_maximum":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"total_award":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":147,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"minimum_amount":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":163,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"bookingType":{"x-edoobox-knownInputs":["all","normal","offerlist","waitinglist"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["all"],"x-edoobox-sourceLine":179},"allowedDomains":{"description":"Retains strings passing email check or FILTER_VALIDATE_URL(http://+entry). Invalid entries filtered, not rejected; non-array becomes[]. Original keys retained, so sparse result can output object.\n\nNon-array ->[]. Email or prepended-http URL filter; array_filter preserves numeric/string keys, so response may be list or keyed object. Retained members are not cast: PHP filter/concatenation may coerce nonstring members during checks, but original values remain in encoded output.\n\nScope / limitations: URL-like validation is not strict DNS domain validation; no rejecting hostname/uniqueItems rule.","x-edoobox-canonicalType":"array","x-edoobox-canonicalItems":"string","x-edoobox-schemaStatus":"source-reviewed","examples":[["member@example.invalid","example.invalid"]],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces include/exclude tag associations. Requires a JSON list; every typed public tag must exist in the selected account. Unknown modes and any invalid/foreign member reject before relation writes. Duplicate mode/tag pairs are normalized; [] clears.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":false,"code_alias":{"x-edoobox-canonicalType":"string","description":"Trim/delete chars outside A-Z/a-z/0-9/_/-, then aP(...,true,10). Invalid characters/excess length normalize, not reject.\n\npreg_replace removes disallowed characters; aP truncates resulting escaped string to10.","x-edoobox-schemaStatus":"source-reviewed","examples":["SAVE_30"],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"settlement":{"x-edoobox-knownInputs":["percent","amount"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["percent"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":303},"validity_range":{"type":"array","items":{"type":"string","pattern":"^(?:offer|category)_[a-f0-9]{12}_-?[0-9]+$","description":"Public offer or category v2 ID returned for an existing same-account course or folder/root2 respectively."},"description":"JSON list of account-owned existing offers/categories. Complete typed/resource-subtype validation happens before validity relations change; duplicate targets are normalized and [] clears the range.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["currency"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Kampagne und Gültigkeitszuordnungen ändern","value":{"data":{"status":false},"id":"promotion_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotion.php","method":"updatePromotion","line":424},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"promotion","short":"prom","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/promotion/{id} Rabatt- und Gutscheinkampagnen abrufen Liest Kampagnen mit Zeitraum, Rabattwert, Mindestbetrag, Buchungsgrenzen und Angebots-/Tagzuordnungen. offer- und category-Filter berücksichtigen den Angebotsbaum. fields possibleUses bzw. amountUses ergänzt Code- und Verwendungszahlen. - Operation-ID: get_v2_promotion_id - Dokumentation: /docs/api/v2/operations/get-v2-promotion-id-286f92fa657e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/promotion/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotion/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotion/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_promotion_id","summary":"Rabatt- und Gutscheinkampagnen abrufen","description":"Liest Kampagnen mit Zeitraum, Rabattwert, Mindestbetrag, Buchungsgrenzen und Angebots-/Tagzuordnungen. offer- und category-Filter berücksichtigen den Angebotsbaum. fields possibleUses bzw. amountUses ergänzt Code- und Verwendungszahlen.","tags":["promotion"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"promotion_012345abcdef_123456":{"id":"promotion_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotion.php","method":"getPromotion","line":455},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"promotion","short":"prom","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/promotion/{id} Kampagne und Gültigkeitszuordnungen ändern Ändert eine Kampagne. currency bleibt unveränderlich. Übergebene validity_range- und tags-Listen ersetzen jeweils alle Zuordnungen; [] entfernt sie. Die Hooks prüfen alle IDs und Kontozuordnungen vor der Änderung. - Operation-ID: put_v2_promotion_id - Dokumentation: /docs/api/v2/operations/put-v2-promotion-id-a7d53c3c5ad9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/promotion/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotion/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotion/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_promotion_id","summary":"Kampagne und Gültigkeitszuordnungen ändern","description":"Ändert eine Kampagne. currency bleibt unveränderlich. Übergebene validity_range- und tags-Listen ersetzen jeweils alle Zuordnungen; [] entfernt sie. Die Hooks prüfen alle IDs und Kontozuordnungen vor der Änderung.","tags":["promotion"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Generische Update-Bestätigung mit update und öffentlicher ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"promotion_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":34},"status":{"x-edoobox-canonicalType":"boolean","description":"PHP value==false ->0 else1. Canonical booleans supported; other values coerce. No strict wire bool validator.\n\nLoose false comparison. insert_default injects input0 when absent, so omitted status defaults inactive despite metadata flagtrue.","x-edoobox-schemaStatus":"source-reviewed","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"creation":{"x-edoobox-knownInputs":["campaign","sale"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["campaign"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":57},"offsetting":{"x-edoobox-knownInputs":["offer","total"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["offer"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":81},"date_start":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":105},"date_end":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":118},"booking_maximum":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":131,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"total_award":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":147,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"minimum_amount":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":163,"x-edoobox-canonicalType":"number","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"bookingType":{"x-edoobox-knownInputs":["all","normal","offerlist","waitinglist"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["all"],"x-edoobox-sourceLine":179},"allowedDomains":{"description":"Retains strings passing email check or FILTER_VALIDATE_URL(http://+entry). Invalid entries filtered, not rejected; non-array becomes[]. Original keys retained, so sparse result can output object.\n\nNon-array ->[]. Email or prepended-http URL filter; array_filter preserves numeric/string keys, so response may be list or keyed object. Retained members are not cast: PHP filter/concatenation may coerce nonstring members during checks, but original values remain in encoded output.\n\nScope / limitations: URL-like validation is not strict DNS domain validation; no rejecting hostname/uniqueItems rule.","x-edoobox-canonicalType":"array","x-edoobox-canonicalItems":"string","x-edoobox-schemaStatus":"source-reviewed","examples":[["member@example.invalid","example.invalid"]],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","enum":["include","exclude"]}},"additionalProperties":true,"required":["tag","type"]},"description":"Replaces include/exclude tag associations. Requires a JSON list; every typed public tag must exist in the selected account. Unknown modes and any invalid/foreign member reject before relation writes. Duplicate mode/tag pairs are normalized; [] clears.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":false,"code_alias":{"x-edoobox-canonicalType":"string","description":"Trim/delete chars outside A-Z/a-z/0-9/_/-, then aP(...,true,10). Invalid characters/excess length normalize, not reject.\n\npreg_replace removes disallowed characters; aP truncates resulting escaped string to10.","x-edoobox-schemaStatus":"source-reviewed","examples":["SAVE_30"],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"settlement":{"x-edoobox-knownInputs":["percent","amount"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["percent"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":303},"validity_range":{"type":"array","items":{"type":"string","pattern":"^(?:offer|category)_[a-f0-9]{12}_-?[0-9]+$","description":"Public offer or category v2 ID returned for an existing same-account course or folder/root2 respectively."},"description":"JSON list of account-owned existing offers/categories. Complete typed/resource-subtype validation happens before validity relations change; duplicate targets are normalized and [] clears the range.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["currency"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kampagne und Gültigkeitszuordnungen ändern","value":{"data":{"status":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotion.php","method":"updatePromotion","line":424},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"promotion","short":"prom","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/promotion/list Rabatt- und Gutscheinkampagnen abrufen Liest Kampagnen mit Zeitraum, Rabattwert, Mindestbetrag, Buchungsgrenzen und Angebots-/Tagzuordnungen. offer- und category-Filter berücksichtigen den Angebotsbaum. fields possibleUses bzw. amountUses ergänzt Code- und Verwendungszahlen. - Operation-ID: get_v2_promotion_list - Dokumentation: /docs/api/v2/operations/get-v2-promotion-list-02667bd71c1e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/promotion/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotion/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotion/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_promotion_list","summary":"Rabatt- und Gutscheinkampagnen abrufen","description":"Liest Kampagnen mit Zeitraum, Rabattwert, Mindestbetrag, Buchungsgrenzen und Angebots-/Tagzuordnungen. offer- und category-Filter berücksichtigen den Angebotsbaum. fields possibleUses bzw. amountUses ergänzt Code- und Verwendungszahlen.","tags":["promotion"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotion_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"promotion_012345abcdef_123456":{"id":"promotion_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"string"},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":47,"lineStart":47,"lineEnd":56,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"31b6ea0be1da051504db1496e9946546b195564680941add5f059f815bc696ca","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"creation":{"enum":["campaign","sale"]},"offsetting":{"enum":["offer","total"]},"date_start":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":105,"anyOf":[{"type":"string"},{"const":false}]},"date_end":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]},"booking_maximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":131,"type":"integer"},"total_award":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":147},"minimum_amount":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":163},"bookingType":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":179},"allowedDomains":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Filter retains original entry values and original keys; it does not cast retained members to strings. Canonical entries are email/domain strings, but the response shape cannot assert all retained values are strings.","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":199,"lineStart":199,"lineEnd":224,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"0cb8eb465f78c11358b80b495bbee4a96232b89c3911d60087e8055ca58d0bcd","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":190,"lineEnd":194,"reason":"The tag callback validates the full replacement before reading/changing existing relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":268,"lineEnd":295,"reason":"Strict list and include/exclude modes, typed tag IDs and complete same-account existence check; invalid input throws ED80100 promotion_tags_invalid.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":241,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]},"currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"code_alias":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotion.php","line":289,"lineStart":289,"lineEnd":302,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"98c33f91ed6539e507c58ca2df466b5f67601955f7ff8bc1178d92c596fb1f72","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual aP normalization, no rejecting maxLength. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotion.php","line":408,"lineStart":408,"lineEnd":431,"sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837","spanSha256":"83a60a84e14eee2a9bc3c9e7fed5dcf05c188b7b259acb6a554a1a309a228e7b","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"settlement":{"enum":["percent","amount"]},"validity_range":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotionfunc.php","line":58,"lineEnd":62,"reason":"PUT validates the full range before changing existing validity relations.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":124,"lineEnd":126,"reason":"POST relation callback uses the same validator.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotionfunc.php","line":150,"lineEnd":180,"reason":"Only typed offer/category IDs, list shape and positive decoded references; same-account DB checks distinguish kurs from ordner/root2.","sha256":"e3297b0b3eb11dce16d9c360da859ed2f2f6a8b0fd6a6eedb1f352b94695a7f0"},{"file":"application/models/v2/Promotion.php","line":343,"reason":"Response returns an array of public offer/category IDs.","sha256":"ae40bd3c823c841d443c8b4b2c089c1ecd8f75d97977e7ea7fc7fc5d26d8a837"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotion.php","method":"getPromotion","line":455},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"promotion","short":"prom","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/promotioncode Promotionscodes und Verwendungen abrufen Liest einzelne Codes mit Aktivstatus, Promotion und Buchungsverwendungen. Pfad-ID, Filter und Feldauswahl begrenzen die Codebestände einer Kampagne. - Operation-ID: get_v2_promotioncode - Dokumentation: /docs/api/v2/operations/get-v2-promotioncode-5d3900af6b33/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/promotioncode' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotioncode"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotioncode" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_promotioncode","summary":"Promotionscodes und Verwendungen abrufen","description":"Liest einzelne Codes mit Aktivstatus, Promotion und Buchungsverwendungen. Pfad-ID, Filter und Feldauswahl begrenzen die Codebestände einer Kampagne.","tags":["promotioncode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"promotioncode_012345abcdef_123456":{"id":"promotioncode_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotioncode.php","method":"getPromotioncode","line":183},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"promotioncode","short":"prco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/promotioncode Promotionscode anlegen Erstellt einen Code für eine bestehende Promotion. status, promotion und code sind erforderlich. Der Hook prüft die Eindeutigkeit mit dem originalen Code nach aP-Normalisierung; die vorläufige Structure-Kürzung begrenzt daher nicht die tatsächliche Eingabelänge. Ein Objekt oder eine generische Liste von Objekten wird unterstützt. - Operation-ID: post_v2_promotioncode - Dokumentation: /docs/api/v2/operations/post-v2-promotioncode-96cd4ac7a2ee/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": true, "promotion": "promotion_012345abcdef_123456", "code": "HERBST-EXAMPLE" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/promotioncode' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotioncode"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": true, "promotion": "promotion_012345abcdef_123456", "code": "HERBST-EXAMPLE" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotioncode" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":true,\"promotion\":\"promotion_012345abcdef_123456\",\"code\":\"HERBST-EXAMPLE\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_promotioncode","summary":"Promotionscode anlegen","description":"Erstellt einen Code für eine bestehende Promotion. status, promotion und code sind erforderlich. Der Hook prüft die Eindeutigkeit mit dem originalen Code nach aP-Normalisierung; die vorläufige Structure-Kürzung begrenzt daher nicht die tatsächliche Eingabelänge. Ein Objekt oder eine generische Liste von Objekten wird unterstützt.","tags":["promotioncode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Generische Insert-Bestätigung mit insert und öffentlicher ID bzw. mehreren IDs bei Batch-Anlage.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"promotioncode_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":34,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"promotion":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":47},"code":{"type":"string","description":"Normal POST ultimately stores aP(originalCode,true) after account duplicate check. Before-insert overwrites initial sanitize/truncate15; final aP truncates250 and does not enforce initial character allowlist.\n\nInitial getter strips chars/truncates15, but _before_addCode validates and rewrites original value with default aP length250.\n\nRuntime preconditions / rejection: Account-scoped duplicate on aP(originalCode,true) -> ED80020/already_exists. No rejecting 15-char max/allowlist applies to final stored value.\n\nScope / limitations: POST many generates from number/promotion/status and ignores client code.","x-edoobox-schemaStatus":"source-reviewed","examples":["SYNTHETIC SAVE 2026!"],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["status","promotion","code"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":34,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"promotion":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":47},"code":{"type":"string","description":"Normal POST ultimately stores aP(originalCode,true) after account duplicate check. Before-insert overwrites initial sanitize/truncate15; final aP truncates250 and does not enforce initial character allowlist.\n\nInitial getter strips chars/truncates15, but _before_addCode validates and rewrites original value with default aP length250.\n\nRuntime preconditions / rejection: Account-scoped duplicate on aP(originalCode,true) -> ED80020/already_exists. No rejecting 15-char max/allowlist applies to final stored value.\n\nScope / limitations: POST many generates from number/promotion/status and ignores client code.","x-edoobox-schemaStatus":"source-reviewed","examples":["SYNTHETIC SAVE 2026!"],"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["status","promotion","code"]}}],"x-edoobox-readOnlyFields":["booking"],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Promotionscode anlegen","value":{"data":{"status":true,"promotion":"promotion_012345abcdef_123456","code":"HERBST-EXAMPLE"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotioncode.php","method":"postPromotioncode","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"promotioncode","short":"prco","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/promotioncode Code-Aktivstatus ändern oder Code-Eindeutigkeit prüfen Ändert den Aktivstatus; promotion, code und booking bleiben unveränderlich. Mit dem speziellen Selektor id=validate wird stattdessen nur data.code auf Eindeutigkeit geprüft: valid=true oder Fehler already_exists, ohne einen Code zu schreiben. - Operation-ID: put_v2_promotioncode - Dokumentation: /docs/api/v2/operations/put-v2-promotioncode-b2f0f292976a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false }, "id": "promotioncode_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/promotioncode' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotioncode"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false }, "id": "promotioncode_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotioncode" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false},\"id\":\"promotioncode_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_promotioncode","summary":"Code-Aktivstatus ändern oder Code-Eindeutigkeit prüfen","description":"Ändert den Aktivstatus; promotion, code und booking bleiben unveränderlich. Mit dem speziellen Selektor id=validate wird stattdessen nur data.code auf Eindeutigkeit geprüft: valid=true oder Fehler already_exists, ohne einen Code zu schreiben.","tags":["promotioncode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Generische Update-Bestätigung mit update und öffentlicher ID. Für validate stattdessen data.valid=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"promotioncode_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":34,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"promotion":false,"code":false,"booking":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["promotion","code","booking"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},{"type":"object","properties":{"code":{"type":"string"}},"additionalProperties":true,"required":["code"]}]},"id":{"anyOf":[{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","examples":["promotioncode_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":"validate"}]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Code-Aktivstatus ändern oder Code-Eindeutigkeit prüfen","value":{"data":{"status":false},"id":"promotioncode_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotioncode.php","method":"updatePromotioncode","line":147},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"promotioncode","short":"prco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/promotioncode/{id} Promotionscodes und Verwendungen abrufen Liest einzelne Codes mit Aktivstatus, Promotion und Buchungsverwendungen. Pfad-ID, Filter und Feldauswahl begrenzen die Codebestände einer Kampagne. - Operation-ID: get_v2_promotioncode_id - Dokumentation: /docs/api/v2/operations/get-v2-promotioncode-id-ee2f7e3bf1dd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/promotioncode/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotioncode/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotioncode/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_promotioncode_id","summary":"Promotionscodes und Verwendungen abrufen","description":"Liest einzelne Codes mit Aktivstatus, Promotion und Buchungsverwendungen. Pfad-ID, Filter und Feldauswahl begrenzen die Codebestände einer Kampagne.","tags":["promotioncode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"promotioncode_012345abcdef_123456":{"id":"promotioncode_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotioncode.php","method":"getPromotioncode","line":183},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"promotioncode","short":"prco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/promotioncode/{id} Code-Aktivstatus ändern oder Code-Eindeutigkeit prüfen Ändert den Aktivstatus; promotion, code und booking bleiben unveränderlich. Mit dem speziellen Selektor id=validate wird stattdessen nur data.code auf Eindeutigkeit geprüft: valid=true oder Fehler already_exists, ohne einen Code zu schreiben. - Operation-ID: put_v2_promotioncode_id - Dokumentation: /docs/api/v2/operations/put-v2-promotioncode-id-25ac62ce760d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Code-v2-ID oder validate für reine Eindeutigkeitsprüfung. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Code-v2-ID oder validate für reine Eindeutigkeitsprüfung.","schema":{"anyOf":[{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","examples":["promotioncode_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":"validate"}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/promotioncode/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotioncode/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotioncode/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_promotioncode_id","summary":"Code-Aktivstatus ändern oder Code-Eindeutigkeit prüfen","description":"Ändert den Aktivstatus; promotion, code und booking bleiben unveränderlich. Mit dem speziellen Selektor id=validate wird stattdessen nur data.code auf Eindeutigkeit geprüft: valid=true oder Fehler already_exists, ohne einen Code zu schreiben.","tags":["promotioncode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Code-v2-ID oder validate für reine Eindeutigkeitsprüfung.","schema":{"anyOf":[{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","examples":["promotioncode_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":"validate"}]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Generische Update-Bestätigung mit update und öffentlicher ID. Für validate stattdessen data.valid=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"promotioncode_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":34,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"promotion":false,"code":false,"booking":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["promotion","code","booking"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},{"type":"object","properties":{"code":{"type":"string"}},"additionalProperties":true,"required":["code"]}]},"id":{"anyOf":[{"type":"string","pattern":"^promotioncode_[a-f0-9]{12}_-?[0-9]+$","examples":["promotioncode_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},{"const":"validate"}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Code-Aktivstatus ändern oder Code-Eindeutigkeit prüfen","value":{"data":{"status":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotioncode.php","method":"updatePromotioncode","line":147},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"promotioncode","short":"prco","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/promotioncode/list Promotionscodes und Verwendungen abrufen Liest einzelne Codes mit Aktivstatus, Promotion und Buchungsverwendungen. Pfad-ID, Filter und Feldauswahl begrenzen die Codebestände einer Kampagne. - Operation-ID: get_v2_promotioncode_list - Dokumentation: /docs/api/v2/operations/get-v2-promotioncode-list-61dd2b2df685/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/promotioncode/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotioncode/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotioncode/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_promotioncode_list","summary":"Promotionscodes und Verwendungen abrufen","description":"Liest einzelne Codes mit Aktivstatus, Promotion und Buchungsverwendungen. Pfad-ID, Filter und Feldauswahl begrenzen die Codebestände einer Kampagne.","tags":["promotioncode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"promotioncode_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"promotioncode_012345abcdef_123456":{"id":"promotioncode_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotioncode_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":34,"type":"boolean"},"promotion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":47,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["promotion_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"code":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":61,"lineStart":61,"lineEnd":75,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"40325f1b849e931ac05f98a66872bd4d1d8a51a2e0830592829050256d924b3f","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Promotioncodefunc.php","line":16,"lineStart":16,"lineEnd":22,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"eb183c7fe702b049bf34b81a8a2ef04847d79af5218b27e2a5c0cb7134c3a488","reason":"Before-insert rewrites original code after validation, aP default250."},{"file":"application/models/v2/Promotioncodefunc.php","line":143,"lineStart":143,"lineEnd":160,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"941c4fd494549700f3af77f91b3f094c534b7791cb4a96ba3c11d2721ff4f218","reason":"Duplicate error."},{"file":"application/models/v2/Promotioncodefunc.php","line":175,"lineStart":175,"lineEnd":185,"sha256":"893680700f0c45976388b1037ee89e448b85f6de5f05bf721765212fc3d39a39","spanSha256":"dccc1ccb738ab6c2170d5e7a05d385b45b3b1f08ef85f938bda7aab874bc38b5","reason":"Account-scoped duplicate query with normalized original code."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"Actual final aP default250 normalization. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Promotioncode.php","line":124,"lineStart":124,"lineEnd":137,"sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976","spanSha256":"0500c9f8b4248f48cf999fdc6c6c8da33a13b60cd198381f03760b6043f27c13","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"booking":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Promotioncode.php","line":92,"reason":"Response maps joined bookings to a public booking-ID array.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"},{"file":"application/models/v2/Promotioncode.php","line":107,"reason":"Association is not writable through generic create/update.","sha256":"e09f4eff13069f33bb1c397c234862172e9717ac220101f56f313da28f132976"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotioncode.php","method":"getPromotioncode","line":183},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"promotioncode","short":"prco","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/promotioncode/list/many Zufällige Promotionscodes für eine Kampagne erzeugen Erzeugt Codes mit dem code_alias der vorhandenen Kampagne als Präfix. status, promotion und number müssen nach Normalisierung wahr bzw. nicht-null sein. number darf höchstens 500 sein und wird für die Schleife ganzzahlig verwendet. Die Antwort enthält nur die Anzahl; die Codes anschließend mit GET lesen. - Operation-ID: post_v2_promotioncode_list_many - Dokumentation: /docs/api/v2/operations/post-v2-promotioncode-list-many-1dca67d1ef3e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": true, "promotion": "promotion_012345abcdef_123456", "number": 10 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/promotioncode/list/many' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/promotioncode/list/many"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": true, "promotion": "promotion_012345abcdef_123456", "number": 10 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/promotioncode/list/many" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":true,\"promotion\":\"promotion_012345abcdef_123456\",\"number\":10}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_promotioncode_list_many","summary":"Zufällige Promotionscodes für eine Kampagne erzeugen","description":"Erzeugt Codes mit dem code_alias der vorhandenen Kampagne als Präfix. status, promotion und number müssen nach Normalisierung wahr bzw. nicht-null sein. number darf höchstens 500 sein und wird für die Schleife ganzzahlig verwendet. Die Antwort enthält nur die Anzahl; die Codes anschließend mit GET lesen.","tags":["promotioncode"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"number":10}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"number":{"type":"integer"}},"additionalProperties":true,"required":["insert","number"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":["boolean","number","string","null"],"description":"Serverseitige Wahrheitswert-Normalisierung; verwenden Sie vorzugsweise JSON true oder false."},"promotion":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto; Beispiel ist synthetisch."},"number":{"type":["number","string"],"description":"Zahl oder numerische Zeichenkette, wird numerisch normalisiert."}},"additionalProperties":true,"required":["status","promotion","number"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zufällige Promotionscodes für eine Kampagne erzeugen","value":{"data":{"status":true,"promotion":"promotion_012345abcdef_123456","number":10}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/08-subscriptions.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Promotioncode.php","method":"postPromotioncode","line":124,"branch":{"file":"application/models/v2/Promotioncode.php","line":131}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"promotioncode","short":"prco","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/publicfile öffentliche Datei löschen Entfernt das öffentliche Speicherobjekt und markiert dessen Metadaten als gelöscht. Fehlende oder bereits entfernte aktive Datensätze ergeben delete=false; Speicherfehler werden als Fehler gemeldet. - Operation-ID: delete_v2_publicfile - Dokumentation: /docs/api/v2/operations/delete-v2-publicfile-79f9e5be38b9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | ja | Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":true,"schema":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"description":"Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_ID:?QUERY_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "publicfile_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/publicfile' \ --url-query 'id='"${QUERY_ID}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/publicfile"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("id", required("QUERY_ID")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "publicfile_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/publicfile" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("id", os.environ["QUERY_ID"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"publicfile_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_publicfile","summary":"öffentliche Datei löschen","description":"Entfernt das öffentliche Speicherobjekt und markiert dessen Metadaten als gelöscht. Fehlende oder bereits entfernte aktive Datensätze ergeben delete=false; Speicherfehler werden als Fehler gemeldet.","tags":["publicfile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"description":"Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete geben die angefragte Datei bzw. das Objekt und das Ergebnis an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"publicfile_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"öffentliche Datei löschen","value":{"id":"publicfile_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Publicfile.php","method":"deletePublicfile","line":179},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"publicfile","short":"pufi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/publicfile öffentliche Datei auflisten Liest Metadaten der aktiven öffentlichen Dateien, etwa Anzeigename, CDN-Dateiname, MIME-Typ, Größe und Erstellungsdatum. Gelöschte Dateien sind ausgeschlossen. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_publicfile - Dokumentation: /docs/api/v2/operations/get-v2-publicfile-db7a7eaff160/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/publicfile' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/publicfile"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/publicfile" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_publicfile","summary":"öffentliche Datei auflisten","description":"Liest Metadaten der aktiven öffentlichen Dateien, etwa Anzeigename, CDN-Dateiname, MIME-Typ, Größe und Erstellungsdatum. Gelöschte Dateien sind ausgeschlossen. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["publicfile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"publicfile_012345abcdef_123456":{"id":"publicfile_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":94,"type":"integer"},"file_storage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":108,"type":"string"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":94,"type":"integer"},"file_storage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":108,"type":"string"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Publicfile.php","method":"getPublicfile","line":195},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"publicfile","short":"pufi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/publicfile öffentliche Datei anlegen Reserviert eine öffentliche Datei und liefert upload_url sowie die spätere cdn_url. Datei-Bytes werden anschließend separat mit dem angegebenen MIME-Typ hochgeladen. file_size wird im Request in Bytes angegeben und in den Metadaten in gerundeten MiB gespeichert; GET normalisiert diesen gespeicherten Wert zusätzlich zu int. Die Erweiterung wird aus file_name abgeleitet; enforce_content_length=true bindet die Bytezahl an die Upload-Signatur. - Operation-ID: post_v2_publicfile - Dokumentation: /docs/api/v2/operations/post-v2-publicfile-27d778580e09/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "file_name": "example.pdf", "file_type": "application/pdf", "file_size": 128 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/publicfile' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/publicfile"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "file_name": "example.pdf", "file_type": "application/pdf", "file_size": 128 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/publicfile" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"file_name\":\"example.pdf\",\"file_type\":\"application/pdf\",\"file_size\":128}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_publicfile","summary":"öffentliche Datei anlegen","description":"Reserviert eine öffentliche Datei und liefert upload_url sowie die spätere cdn_url. Datei-Bytes werden anschließend separat mit dem angegebenen MIME-Typ hochgeladen. file_size wird im Request in Bytes angegeben und in den Metadaten in gerundeten MiB gespeichert; GET normalisiert diesen gespeicherten Wert zusätzlich zu int. Die Erweiterung wird aus file_name abgeleitet; enforce_content_length=true bindet die Bytezahl an die Upload-Signatur.","tags":["publicfile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert, öffentliche id und bei Uploads signierte upload_url; öffentliche Dateien liefern zusätzlich cdn_url.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"publicfile_012345abcdef_123456","upload_url":"https://storage.example.invalid/synthetic-upload","cdn_url":"https://cdn.example.invalid/synthetic.pdf"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"insert":{"type":"boolean"},"upload_url":{"type":"string","format":"uri","description":"Signierte Upload-URL; vertraulich und zeitlich begrenzt."},"cdn_url":{"type":"string","format":"uri"}},"additionalProperties":true,"required":["id","insert","upload_url","cdn_url"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"file_name":{"type":"string","description":"The extension is lowercased from normalized file_name; supplied file_extension is ignored. The actual derived extension must have at most5 characters and contain no slash/backslash, C0/DEL or percent-encoded byte sequence. Public CDN names also reject any ?, # or % in that extension."},"file_type":{"type":"string","maxLength":100,"description":"Native getter rejects raw UTF-8 length over100 and normalized SQL-escaped storage length over100. Quote/backslash-rich input can reach the encoded limit earlier than the raw limit."},"file_size":{"x-edoobox-canonicalType":"integer","description":"Native integer normalization; the reviewed upload workflow independently verifies integral byte transfer.","examples":[128]},"enforce_content_length":{"description":"Only strict true asks the native signer to bind ContentLength."},"file_extension":{"description":"Ignored by native create; the extension is derived from normalized file_name.","x-edoobox-ignored-input":true}},"required":["file_name","file_type","file_size"],"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"value":{"data":{"file_name":"example.pdf","file_type":"application/pdf","file_size":128}}}}}}},"x-edoobox-upload-ticket":{"provider":"s3","bucket":"cdn-data01","regionEnv":"AWS_S3_REGION_PUBLICFILE","urlPointer":"/data/upload_url","pathPrefixTemplate":"/accountdata/{account_id}/","method":"PUT","expiresInSeconds":1800,"source":{"file":"application/models/v2/Publicfilefunc.php","line":49,"sha256":"fe2d97ee0566087a041d888ed3102aa91896025df9146d0db651a3a6d0be82ab","method":"_postFile"},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":false,"scope":"object","readback":{"operationId":"get_v2_publicfile_id","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"proves":"metadata-only"},"abort":{"operationId":"delete_v2_publicfile_id","argumentBindings":[{"target":"/path/id","source":"/data/id"}],"storageCleanup":"native-delete-attempt"},"notes":["The metadata row is created before the storage PUT. No API completion or upload-status operation exists for this resource.","Metadata readback does not prove storage object existence or byte integrity. A ticket discard does not delete native metadata.","Abort is a separate mutation requiring normal preview, approval and current authorization. Never automatically retry a PUT after an ambiguous outcome.","Native deletion does not revoke a previously issued presigned URL. Do not race an in-flight transfer with abort; a late PUT can recreate an orphaned object."]}},"x-edoobox-upload-input-review":{"derivedExtensionLimit":5,"providedExtension":"ignored","nativeValidation":"The extension is lowercased from normalized file_name; supplied file_extension is ignored. The actual derived extension must have at most5 characters and contain no slash/backslash, C0/DEL or percent-encoded byte sequence. Public CDN names also reject any ?, # or % in that extension.","source":{"file":"application/models/v2/Publicfilefunc.php","line":80,"sha256":"fe2d97ee0566087a041d888ed3102aa91896025df9146d0db651a3a6d0be82ab","lineEnd":94},"relatedSource":{"file":"application/models/v2/Publicfile.php","line":76,"sha256":"716dd641bae8b3b14b1ca9883021ff9275cf04896a2db1214f79ca161c91a1b5"}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Publicfile.php","method":"postPublicfile","line":145},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"publicfile","short":"pufi","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/publicfile öffentliche Datei ändern Ändert den Anzeigenamen einer öffentlichen Datei. CDN-Dateiname, Speicherort, MIME-Typ, Erweiterung und gespeicherte Größe bleiben unveränderlich; Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_publicfile - Dokumentation: /docs/api/v2/operations/put-v2-publicfile-c3f219bd734e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "file_name": "Unterlagen.pdf" }, "id": "publicfile_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/publicfile' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/publicfile"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "file_name": "Unterlagen.pdf" }, "id": "publicfile_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/publicfile" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"file_name\":\"Unterlagen.pdf\"},\"id\":\"publicfile_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_publicfile","summary":"öffentliche Datei ändern","description":"Ändert den Anzeigenamen einer öffentlichen Datei. CDN-Dateiname, Speicherort, MIME-Typ, Erweiterung und gespeicherte Größe bleiben unveränderlich; Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["publicfile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"publicfile_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":31},"file_name_cdn":false,"file_extension":false,"file_type":false,"file_size":false,"file_storage":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["file_name_cdn","file_extension","file_type","file_size","file_storage","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"öffentliche Datei ändern","value":{"data":{"file_name":"Unterlagen.pdf"},"id":"publicfile_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Publicfile.php","method":"updatePublicfile","line":162},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"publicfile","short":"pufi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/publicfile/{id} öffentliche Datei löschen Entfernt das öffentliche Speicherobjekt und markiert dessen Metadaten als gelöscht. Fehlende oder bereits entfernte aktive Datensätze ergeben delete=false; Speicherfehler werden als Fehler gemeldet. Die Datei wird über die Pfad-ID ausgewählt; ein Request-Body ist für diese Route nicht erforderlich. - Operation-ID: delete_v2_publicfile_id - Dokumentation: /docs/api/v2/operations/delete-v2-publicfile-id-4f87fa39f3f3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["publicfile_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/publicfile/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/publicfile/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "DELETE", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/publicfile/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_publicfile_id","summary":"öffentliche Datei löschen","description":"Entfernt das öffentliche Speicherobjekt und markiert dessen Metadaten als gelöscht. Fehlende oder bereits entfernte aktive Datensätze ergeben delete=false; Speicherfehler werden als Fehler gemeldet. Die Datei wird über die Pfad-ID ausgewählt; ein Request-Body ist für diese Route nicht erforderlich.","tags":["publicfile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["publicfile_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete geben die angefragte Datei bzw. das Objekt und das Ergebnis an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"publicfile_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Publicfile.php","method":"deletePublicfile","line":179},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"publicfile","short":"pufi","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/publicfile/{id} öffentliche Datei lesen Liest Metadaten der aktiven öffentlichen Dateien, etwa Anzeigename, CDN-Dateiname, MIME-Typ, Größe und Erstellungsdatum. Gelöschte Dateien sind ausgeschlossen. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_publicfile_id - Dokumentation: /docs/api/v2/operations/get-v2-publicfile-id-3d497f7837e2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["publicfile_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/publicfile/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/publicfile/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/publicfile/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_publicfile_id","summary":"öffentliche Datei lesen","description":"Liest Metadaten der aktiven öffentlichen Dateien, etwa Anzeigename, CDN-Dateiname, MIME-Typ, Größe und Erstellungsdatum. Gelöschte Dateien sind ausgeschlossen. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["publicfile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["publicfile_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"publicfile_012345abcdef_123456":{"id":"publicfile_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":94,"type":"integer"},"file_storage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":108,"type":"string"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":94,"type":"integer"},"file_storage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":108,"type":"string"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Publicfile.php","method":"getPublicfile","line":195},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"publicfile","short":"pufi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/publicfile/{id} öffentliche Datei ändern Ändert den Anzeigenamen einer öffentlichen Datei. CDN-Dateiname, Speicherort, MIME-Typ, Erweiterung und gespeicherte Größe bleiben unveränderlich; Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_publicfile_id - Dokumentation: /docs/api/v2/operations/put-v2-publicfile-id-7fa8c307cb2f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["publicfile_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "file_name": "Unterlagen.pdf" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/publicfile/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/publicfile/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "file_name": "Unterlagen.pdf" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/publicfile/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"file_name\":\"Unterlagen.pdf\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_publicfile_id","summary":"öffentliche Datei ändern","description":"Ändert den Anzeigenamen einer öffentlichen Datei. CDN-Dateiname, Speicherort, MIME-Typ, Erweiterung und gespeicherte Größe bleiben unveränderlich; Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["publicfile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["publicfile_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"publicfile_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":31},"file_name_cdn":false,"file_extension":false,"file_type":false,"file_size":false,"file_storage":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["file_name_cdn","file_extension","file_type","file_size","file_storage","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["publicfile_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"öffentliche Datei ändern","value":{"data":{"file_name":"Unterlagen.pdf"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Publicfile.php","method":"updatePublicfile","line":162},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"publicfile","short":"pufi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/publicfile/list öffentliche Datei auflisten Liest Metadaten der aktiven öffentlichen Dateien, etwa Anzeigename, CDN-Dateiname, MIME-Typ, Größe und Erstellungsdatum. Gelöschte Dateien sind ausgeschlossen. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_publicfile_list - Dokumentation: /docs/api/v2/operations/get-v2-publicfile-list-934e9e0d1516/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/publicfile/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/publicfile/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/publicfile/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_publicfile_list","summary":"öffentliche Datei auflisten","description":"Liest Metadaten der aktiven öffentlichen Dateien, etwa Anzeigename, CDN-Dateiname, MIME-Typ, Größe und Erstellungsdatum. Gelöschte Dateien sind ausgeschlossen. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["publicfile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"publicfile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"publicfile_012345abcdef_123456":{"id":"publicfile_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":94,"type":"integer"},"file_storage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":108,"type":"string"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":31,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"string"},"file_extension":{"description":"At most five raw and escaped storage characters; create derives this from file_name.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":59,"type":"string"},"file_type":{"description":"MIME text is rejected above 100 raw or escaped characters, before signing or insertion.\n\nResponse field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":77,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":94,"type":"integer"},"file_storage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":108,"type":"string"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":118,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Publicfile.php","method":"getPublicfile","line":195},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"publicfile","short":"pufi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/pusherwss Privaten WebSocket-Kanal autorisieren Signiert den Pusher-Zugang für channel_name und socket_id aus application/x-www-form-urlencoded. Erlaubt sind private-, presence- und private-edv2services. Presence ergänzt die angemeldete Identität. Die Antwortsignatur ist vertraulich und kein neu angelegtes edoobox-Objekt. - Operation-ID: post_v2_pusherwss - Dokumentation: /docs/api/v2/operations/post-v2-pusherwss-3fc746d48df4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/pusherwss' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pusherwss"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für application/x-www-form-urlencoded vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/pusherwss" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_pusherwss","summary":"Privaten WebSocket-Kanal autorisieren","description":"Signiert den Pusher-Zugang für channel_name und socket_id aus application/x-www-form-urlencoded. Erlaubt sind private-, presence- und private-edv2services. Presence ergänzt die angemeldete Identität. Die Antwortsignatur ist vertraulich und kein neu angelegtes edoobox-Objekt.","tags":["pusherwss"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Authentifizierung ist text/plain. Nicht erlaubte Kanäle liefern gewöhnliche JSON-Fehlerantworten.","content":{"text/plain":{"schema":{"type":"string","description":"Pusher-JSON-Text mit auth, bei Presence auch channel_data."},"example":"{\"auth\":\"example-key:synthetic-signature\"}"}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"channel_name":{"type":"string"},"socket_id":{"type":"string"}},"additionalProperties":true,"required":["channel_name","socket_id"]},"example":{"channel_name":"private-account_012345abcdef_123456","socket_id":"1234.5678"}}}},"x-edoobox-uiInfrastructure":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Pusherwss.php","method":"postPusherwss","line":27},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"pusherwss","short":"pshr","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/pusherwss/list/supportid Support-ID an edoobox-Support senden Veröffentlicht Support-ID und Namen der angemeldeten Person als internes Echtzeitereignis an den Support. Ein zeitlich begrenzter, bereits abgelaufener Supportzugang wird um eine Stunde verlängert; gesperrter oder dauerhaft offener Zugang bleibt unverändert. list/supportid und send/supportid sind dieselbe Aktion. Der Payload wird nicht ausgewertet. - Operation-ID: post_v2_pusherwss_list_supportid - Dokumentation: /docs/api/v2/operations/post-v2-pusherwss-list-supportid-da39e51328dc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/pusherwss/list/supportid' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pusherwss/list/supportid"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pusherwss/list/supportid" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_pusherwss_list_supportid","summary":"Support-ID an edoobox-Support senden","description":"Veröffentlicht Support-ID und Namen der angemeldeten Person als internes Echtzeitereignis an den Support. Ein zeitlich begrenzter, bereits abgelaufener Supportzugang wird um eine Stunde verlängert; gesperrter oder dauerhaft offener Zugang bleibt unverändert. list/supportid und send/supportid sind dieselbe Aktion. Der Payload wird nicht ausgewertet.","tags":["pusherwss"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id ist die öffentliche Konto-ID; insert=true bestätigt die Veröffentlichung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["account_012345abcdef_123456"]},"insert":{"const":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-bodylessSource":{"file":"application/models/v2/Pusherwss.php","line":100},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Support-ID an edoobox-Support senden","value":{}}}}},"required":false},"x-edoobox-source":{"file":"application/models/v2/Pusherwss.php","method":"postPusherwss","line":27,"branch":{"file":"application/models/v2/Pusherwss.php","line":100}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"pusherwss","short":"pshr","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/pusherwss/send/supportid Support-ID an edoobox-Support senden Veröffentlicht Support-ID und Namen der angemeldeten Person als internes Echtzeitereignis an den Support. Ein zeitlich begrenzter, bereits abgelaufener Supportzugang wird um eine Stunde verlängert; gesperrter oder dauerhaft offener Zugang bleibt unverändert. list/supportid und send/supportid sind dieselbe Aktion. Der Payload wird nicht ausgewertet. - Operation-ID: post_v2_pusherwss_send_supportid - Dokumentation: /docs/api/v2/operations/post-v2-pusherwss-send-supportid-af7eb9edf690/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/pusherwss/send/supportid' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/pusherwss/send/supportid"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/pusherwss/send/supportid" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_pusherwss_send_supportid","summary":"Support-ID an edoobox-Support senden","description":"Veröffentlicht Support-ID und Namen der angemeldeten Person als internes Echtzeitereignis an den Support. Ein zeitlich begrenzter, bereits abgelaufener Supportzugang wird um eine Stunde verlängert; gesperrter oder dauerhaft offener Zugang bleibt unverändert. list/supportid und send/supportid sind dieselbe Aktion. Der Payload wird nicht ausgewertet.","tags":["pusherwss"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id ist die öffentliche Konto-ID; insert=true bestätigt die Veröffentlichung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["account_012345abcdef_123456"]},"insert":{"const":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Pusherwss.php","line":100,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-bodylessSource":{"file":"application/models/v2/Pusherwss.php","line":100},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Support-ID an edoobox-Support senden","value":{}}}}},"required":false},"x-edoobox-source":{"file":"application/models/v2/Pusherwss.php","method":"postPusherwss","line":27,"branch":{"file":"application/models/v2/Pusherwss.php","line":100}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"pusherwss","short":"pshr","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/question Antwortoption auflisten Liest Antwortoption-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein. - Operation-ID: get_v2_question - Dokumentation: /docs/api/v2/operations/get-v2-question-afe4b264a170/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/question' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/question"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/question" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_question","summary":"Antwortoption auflisten","description":"Liest Antwortoption-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein.","tags":["question"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"question_012345abcdef_123456":{"id":"question_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},"status":{"type":"boolean"},"questiongroup":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"name":{"type":"string"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},"status":{"type":"boolean"},"questiongroup":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"name":{"type":"string"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Question.php","method":"getQuestion","line":165},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"question","short":"ques","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/question Antwortoption erstellen Erstellt Antwortoption aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft. - Operation-ID: post_v2_question - Dokumentation: /docs/api/v2/operations/post-v2-question-e259f4853585/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "questiongroup": "questiongroup_012345abcdef_123456", "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/question' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/question"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "questiongroup": "questiongroup_012345abcdef_123456", "name": "Example" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/question" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"questiongroup\":\"questiongroup_012345abcdef_123456\",\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_question","summary":"Antwortoption erstellen","description":"Erstellt Antwortoption aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft.","tags":["question"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält eine öffentliche ID oder bei Stapelerstellung eine ID-Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"question_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":31,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"questiongroup":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":40},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":54},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":81,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["questiongroup","name"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":31,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"questiongroup":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":40},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":54},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":81,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["questiongroup","name"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["question_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"questiongroup":"questiongroup_012345abcdef_123456","name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Question.php","method":"postQuestion","line":115},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"question","short":"ques","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/question Antwortoption ändern Ändert die übergebenen veränderbaren Felder von Antwortoption. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. - Operation-ID: put_v2_question - Dokumentation: /docs/api/v2/operations/put-v2-question-d7aa0790dd1d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" }, "id": "question_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/question' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/question"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" }, "id": "question_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/question" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"},\"id\":\"question_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_question","summary":"Antwortoption ändern","description":"Ändert die übergebenen veränderbaren Felder von Antwortoption. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert.","tags":["question"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"question_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":31,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"questiongroup":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":54},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":81,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["questiongroup"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["question_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Antwortoption ändern","value":{"data":{"name":"Example"},"id":"question_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Question.php","method":"updateQuestion","line":132},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"question","short":"ques","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/question/{id} Antwortoption lesen Liest Antwortoption anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein. - Operation-ID: get_v2_question_id - Dokumentation: /docs/api/v2/operations/get-v2-question-id-974cb4ed9b00/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["question_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/question/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/question/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/question/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_question_id","summary":"Antwortoption lesen","description":"Liest Antwortoption anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein.","tags":["question"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["question_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"question_012345abcdef_123456":{"id":"question_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},"status":{"type":"boolean"},"questiongroup":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"name":{"type":"string"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},"status":{"type":"boolean"},"questiongroup":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"name":{"type":"string"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Question.php","method":"getQuestion","line":165},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"question","short":"ques","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/question/{id} Antwortoption ändern Ändert die übergebenen veränderbaren Felder von Antwortoption. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. - Operation-ID: put_v2_question_id - Dokumentation: /docs/api/v2/operations/put-v2-question-id-d4f1a81fe585/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["question_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/question/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/question/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/question/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_question_id","summary":"Antwortoption ändern","description":"Ändert die übergebenen veränderbaren Felder von Antwortoption. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert.","tags":["question"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["question_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"question_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":31,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"questiongroup":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":54},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":81,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["questiongroup"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["question_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Antwortoption ändern","value":{"data":{"name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Question.php","method":"updateQuestion","line":132},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"question","short":"ques","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/question/list Antwortoption auflisten Liest Antwortoption-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein. - Operation-ID: get_v2_question_list - Dokumentation: /docs/api/v2/operations/get-v2-question-list-fa1a73e4070c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/question/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/question/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/question/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_question_list","summary":"Antwortoption auflisten","description":"Liest Antwortoption-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. Die übergeordnete Gruppe muss zur passenden Art gehören und darf nicht gelöscht sein.","tags":["question"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"question_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"question_012345abcdef_123456":{"id":"question_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},"status":{"type":"boolean"},"questiongroup":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"name":{"type":"string"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^question_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["question_012345abcdef_123456"]},"status":{"type":"boolean"},"questiongroup":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"name":{"type":"string"},"order":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Question.php","method":"getQuestion","line":165},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"question","short":"ques","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/questiongroup Zusatzfrage löschen Löscht Zusatzfrage, indem der Datensatz als gelöscht markiert wird. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang. - Operation-ID: delete_v2_questiongroup - Dokumentation: /docs/api/v2/operations/delete-v2-questiongroup-cfef183d0f17/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "questiongroup_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/questiongroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/questiongroup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "questiongroup_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/questiongroup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"questiongroup_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_questiongroup","summary":"Zusatzfrage löschen","description":"Löscht Zusatzfrage, indem der Datensatz als gelöscht markiert wird. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang.","tags":["questiongroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id ist die öffentliche ID; delete zeigt eine betroffene Zeile an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"questiongroup_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Zusatzfrage löschen","value":{"id":"questiongroup_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Questiongroup.php","method":"deleteQuestiongroup","line":181},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"questiongroup","short":"queg","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/questiongroup Zusatzfrage auflisten Liest Zusatzfrage-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_questiongroup - Dokumentation: /docs/api/v2/operations/get-v2-questiongroup-b1ca91f70a40/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/questiongroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/questiongroup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/questiongroup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_questiongroup","summary":"Zusatzfrage auflisten","description":"Liest Zusatzfrage-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["questiongroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"questiongroup_012345abcdef_123456":{"id":"questiongroup_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Questiongroup.php","method":"getQuestiongroup","line":197},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"questiongroup","short":"queg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/questiongroup Zusatzfrage erstellen Erstellt Zusatzfrage aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft. response_type: textarea für mehrzeiligen Text, radio für Auswahl; sonst Ausgabe text. Kurzformen area/opt/inp ergeben nicht die entsprechenden Feldarten. - Operation-ID: post_v2_questiongroup - Dokumentation: /docs/api/v2/operations/post-v2-questiongroup-87b3f0a84aaa/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "title": "Zusätzliche Angaben", "response_type": "textarea" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/questiongroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/questiongroup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "title": "Zusätzliche Angaben", "response_type": "textarea" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/questiongroup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"title\":\"Zusätzliche Angaben\",\"response_type\":\"textarea\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_questiongroup","summary":"Zusatzfrage erstellen","description":"Erstellt Zusatzfrage aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft. response_type: textarea für mehrzeiligen Text, radio für Auswahl; sonst Ausgabe text. Kurzformen area/opt/inp ergeben nicht die entsprechenden Feldarten.","tags":["questiongroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält eine öffentliche ID oder bei Stapelerstellung eine ID-Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"questiongroup_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":27,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"title":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":45},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":72},"response_type":{"description":"textarea und radio werden unverändert gespeichert; alle anderen Eingabewerte werden inp (Ausgabe text).","examples":["textarea"],"x-edoobox-canonicalType":"string"}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["title","response_type"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":27,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"title":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":45},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":72},"response_type":{"description":"textarea und radio werden unverändert gespeichert; alle anderen Eingabewerte werden inp (Ausgabe text).","examples":["textarea"],"x-edoobox-canonicalType":"string"}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["title","response_type"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zusatzfrage erstellen","value":{"data":{"title":"Zusätzliche Angaben","response_type":"textarea"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Questiongroup.php","method":"postQuestiongroup","line":147},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"questiongroup","short":"queg","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/questiongroup Zusatzfrage ändern Ändert die übergebenen veränderbaren Felder von Zusatzfrage. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. response_type: textarea für mehrzeiligen Text, radio für Auswahl; sonst Ausgabe text. Kurzformen area/opt/inp ergeben nicht die entsprechenden Feldarten. - Operation-ID: put_v2_questiongroup - Dokumentation: /docs/api/v2/operations/put-v2-questiongroup-b309eec87d80/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "title": "Example" }, "id": "questiongroup_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/questiongroup' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/questiongroup"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "title": "Example" }, "id": "questiongroup_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/questiongroup" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"title\":\"Example\"},\"id\":\"questiongroup_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_questiongroup","summary":"Zusatzfrage ändern","description":"Ändert die übergebenen veränderbaren Felder von Zusatzfrage. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. response_type: textarea für mehrzeiligen Text, radio für Auswahl; sonst Ausgabe text. Kurzformen area/opt/inp ergeben nicht die entsprechenden Feldarten.","tags":["questiongroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"questiongroup_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":27,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"title":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":45},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":72},"response_type":{"description":"textarea und radio werden unverändert gespeichert; alle anderen Eingabewerte werden inp (Ausgabe text).","examples":["textarea"],"x-edoobox-canonicalType":"string"}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Zusatzfrage ändern","value":{"data":{"title":"Example"},"id":"questiongroup_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Questiongroup.php","method":"updateQuestiongroup","line":164},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"questiongroup","short":"queg","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/questiongroup/{id} Zusatzfrage löschen Löscht Zusatzfrage, indem der Datensatz als gelöscht markiert wird. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang. - Operation-ID: delete_v2_questiongroup_id - Dokumentation: /docs/api/v2/operations/delete-v2-questiongroup-id-28b18dcb6604/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "questiongroup_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/questiongroup/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/questiongroup/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "questiongroup_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/questiongroup/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"questiongroup_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_questiongroup_id","summary":"Zusatzfrage löschen","description":"Löscht Zusatzfrage, indem der Datensatz als gelöscht markiert wird. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang.","tags":["questiongroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id ist die öffentliche ID; delete zeigt eine betroffene Zeile an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"questiongroup_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Zusatzfrage löschen","value":{"id":"questiongroup_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Questiongroup.php","method":"deleteQuestiongroup","line":181},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"questiongroup","short":"queg","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/questiongroup/{id} Zusatzfrage lesen Liest Zusatzfrage anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_questiongroup_id - Dokumentation: /docs/api/v2/operations/get-v2-questiongroup-id-1ef8d7b32f03/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/questiongroup/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/questiongroup/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/questiongroup/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_questiongroup_id","summary":"Zusatzfrage lesen","description":"Liest Zusatzfrage anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["questiongroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"questiongroup_012345abcdef_123456":{"id":"questiongroup_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Questiongroup.php","method":"getQuestiongroup","line":197},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"questiongroup","short":"queg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/questiongroup/{id} Zusatzfrage ändern Ändert die übergebenen veränderbaren Felder von Zusatzfrage. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. response_type: textarea für mehrzeiligen Text, radio für Auswahl; sonst Ausgabe text. Kurzformen area/opt/inp ergeben nicht die entsprechenden Feldarten. - Operation-ID: put_v2_questiongroup_id - Dokumentation: /docs/api/v2/operations/put-v2-questiongroup-id-2844869d1c9e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "title": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/questiongroup/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/questiongroup/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "title": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/questiongroup/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"title\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_questiongroup_id","summary":"Zusatzfrage ändern","description":"Ändert die übergebenen veränderbaren Felder von Zusatzfrage. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. response_type: textarea für mehrzeiligen Text, radio für Auswahl; sonst Ausgabe text. Kurzformen area/opt/inp ergeben nicht die entsprechenden Feldarten.","tags":["questiongroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"questiongroup_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":27,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"default":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":36,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"title":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":45},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":800,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":72},"response_type":{"description":"textarea und radio werden unverändert gespeichert; alle anderen Eingabewerte werden inp (Ausgabe text).","examples":["textarea"],"x-edoobox-canonicalType":"string"}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["questiongroup_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zusatzfrage ändern","value":{"data":{"title":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Questiongroup.php","method":"updateQuestiongroup","line":164},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"questiongroup","short":"queg","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/questiongroup/list Zusatzfrage auflisten Liest Zusatzfrage-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_questiongroup_list - Dokumentation: /docs/api/v2/operations/get-v2-questiongroup-list-0315582b1f96/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/questiongroup/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/questiongroup/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/questiongroup/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_questiongroup_list","summary":"Zusatzfrage auflisten","description":"Liest Zusatzfrage-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["questiongroup"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"questiongroup_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"questiongroup_012345abcdef_123456":{"id":"questiongroup_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^questiongroup_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["questiongroup_012345abcdef_123456"]},"status":{"type":"boolean"},"default":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"response_type":{"type":"string","enum":["textarea","radio","text"]},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"sort":{"type":"integer"}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Questiongroup.php","method":"getQuestiongroup","line":197},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"questiongroup","short":"queg","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/room Räume auflisten Liest Räume im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Berücksichtigt Raumdatensätze mit internem keinZimmer=n oder y. Beim Anlegen wird ein regulärer Raum erzeugt; place ist danach unveränderlich. - Operation-ID: get_v2_room - Dokumentation: /docs/api/v2/operations/get-v2-room-c7aaf58b0c20/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/room' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/room"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/room" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_room","summary":"Räume auflisten","description":"Liest Räume im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Berücksichtigt Raumdatensätze mit internem keinZimmer=n oder y. Beim Anlegen wird ein regulärer Raum erzeugt; place ist danach unveränderlich.","tags":["room"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"room_012345abcdef_123456":{"id":"room_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Room.php","method":"getRoom","line":157},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"room","short":"room","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/room Raum anlegen Legt Raum mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs. - Operation-ID: post_v2_room - Dokumentation: /docs/api/v2/operations/post-v2-room-a62bee74bcdc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example", "place": "place_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/room' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/room"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example", "place": "place_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/room" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\",\"place\":\"place_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_room","summary":"Raum anlegen","description":"Legt Raum mit data an. data kann ein Datensatz oder eine Liste von Datensätzen sein; die im Schema markierten Pflichtfelder gelten für jeden Datensatz. Beziehungen verwenden öffentliche v2-IDs.","tags":["room"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"room_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":40,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"place":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":62},"availability":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":76,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","place"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":40,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"place":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":62},"availability":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":76,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name","place"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"name":"Example","place":"place_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Room.php","method":"postRoom","line":107},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"room","short":"room","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/room Raum aktualisieren Ändert die übergebenen schreibbaren Felder in data für Raum. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden. - Operation-ID: put_v2_room - Dokumentation: /docs/api/v2/operations/put-v2-room-5f95df92cd08/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" }, "id": "room_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/room' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/room"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" }, "id": "room_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/room" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"},\"id\":\"room_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_room","summary":"Raum aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Raum. Die Ziel-ID muss als id im JSON-Umschlag angegeben werden.","tags":["room"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"room_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":40,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"place":false,"availability":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":76,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["place"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Raum aktualisieren","value":{"data":{"name":"Example"},"id":"room_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Room.php","method":"updateRoom","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"room","short":"room","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/room/{id} Raum abrufen Liest Raum anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Berücksichtigt Raumdatensätze mit internem keinZimmer=n oder y. Beim Anlegen wird ein regulärer Raum erzeugt; place ist danach unveränderlich. - Operation-ID: get_v2_room_id - Dokumentation: /docs/api/v2/operations/get-v2-room-id-df25cd511096/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/room/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/room/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/room/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_room_id","summary":"Raum abrufen","description":"Liest Raum anhand der öffentlichen Pfad-ID. Auch diese Einzelabfrage liefert die generische ID-indizierte Ergebnissammlung; fields begrenzt die Rückgabefelder. Berücksichtigt Raumdatensätze mit internem keinZimmer=n oder y. Beim Anlegen wird ein regulärer Raum erzeugt; place ist danach unveränderlich.","tags":["room"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"room_012345abcdef_123456":{"id":"room_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Room.php","method":"getRoom","line":157},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"room","short":"room","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/room/{id} Raum aktualisieren Ändert die übergebenen schreibbaren Felder in data für Raum. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang. - Operation-ID: put_v2_room_id - Dokumentation: /docs/api/v2/operations/put-v2-room-id-6b375787a753/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/room/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/room/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/room/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_room_id","summary":"Raum aktualisieren","description":"Ändert die übergebenen schreibbaren Felder in data für Raum. Die Pfad-ID wählt das Ziel; ein zusätzliches id im JSON-Umschlag hat im Legacy-Handler Vorrang.","tags":["room"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Ergebnis in data mit den beschriebenen Feldern; error und die Ergebniskennzeichen müssen ausgewertet werden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"room_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":40,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"place":false,"availability":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":76,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["place"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Room.php","method":"updateRoom","line":124},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"room","short":"room","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/room/{id}/workload Belegung für Raum abrufen Liefert eine Tabelle der Termine, die den gewünschten Zeitraum überlappen. filter benötigt start und end als parsebare Datumswerte. Die Pfad-ID wird als erster id-Filter eingesetzt. Gelöschte bzw. im Papierkorb befindliche Angebote werden ausgeschlossen. Die Zeilen entsprechen der Reihenfolge in columns. - Operation-ID: get_v2_room_id_workload - Dokumentation: /docs/api/v2/operations/get-v2-room-id-workload-0ffe9a13e77f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Pflichtfilter: start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Pflichtfilter: start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/room/'"${PATH_ID}"'/workload' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/room/{id}/workload"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/room/{id}/workload" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_room_id_workload","summary":"Belegung für Raum abrufen","description":"Liefert eine Tabelle der Termine, die den gewünschten Zeitraum überlappen. filter benötigt start und end als parsebare Datumswerte. Die Pfad-ID wird als erster id-Filter eingesetzt. Gelöschte bzw. im Papierkorb befindliche Angebote werden ausgeschlossen. Die Zeilen entsprechen der Reihenfolge in columns.","tags":["room"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Pflichtfilter: start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"offername","type":"string"},{"field":"name","type":"string"},{"field":"offer_id","type":"string"},{"field":"date_id","type":"string"},{"field":"offerdef_id","type":"string"},{"field":"place_id","type":"string"},{"field":"room_id","type":"string"},{"field":"status","type":"integer"},{"field":"date_start","type":"date"},{"field":"date_end","type":"date"}],"rows":[["Beispielangebot","Einführung","offer_012345abcdef_123456","date_012345abcdef_123456","offerdef_012345abcdef_123456","place_012345abcdef_123456","room_012345abcdef_123456",1,"2026-11-03T09:00:00+01:00","2026-11-03T10:00:00+01:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},{"type":"string","pattern":"^offerdef_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerdef_012345abcdef_123456"]},{"anyOf":[{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},{"const":false}]},{"anyOf":[{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},{"const":false}]},{"type":"integer"},{"anyOf":[{"type":"string"},{"const":false}]},{"anyOf":[{"type":"string"},{"const":false}]}],"minItems":10,"maxItems":10}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Room.php","line":173,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminfunc.php","line":172},"reason":"Workload SELECT and formatting only; Placefunc194/Roomfunc12 likewise."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Room.php","method":"getRoom","line":157,"branch":{"file":"application/models/v2/Room.php","line":173}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"room","short":"room","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/room/list Räume auflisten Liest Räume im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Berücksichtigt Raumdatensätze mit internem keinZimmer=n oder y. Beim Anlegen wird ein regulärer Raum erzeugt; place ist danach unveränderlich. - Operation-ID: get_v2_room_list - Dokumentation: /docs/api/v2/operations/get-v2-room-list-b108467eab6b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/room/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/room/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/room/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_room_list","summary":"Räume auflisten","description":"Liest Räume im gewählten Konto. filter, fields, order und limit steuern Auswahl, Felder, Sortierung und Seitengröße; response_type=array liefert eine Liste. Berücksichtigt Raumdatensätze mit internem keinZimmer=n oder y. Beim Anlegen wird ein regulärer Raum erzeugt; place ist danach unveränderlich.","tags":["room"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"room_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"room_012345abcdef_123456":{"id":"room_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["room_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":40,"type":"boolean"},"name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":49,"type":"string"},"place":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["place_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"availability":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"integer"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Room.php","method":"getRoom","line":157},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"room","short":"room","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/room/list/workload Belegung für Raum abrufen Liefert eine Tabelle der Termine, die den gewünschten Zeitraum überlappen. filter benötigt start und end als parsebare Datumswerte. Auf der Listenroute muss der erste Filter property=id mit einer einzelnen öffentlichen room-ID sein; mehrere Orte/Räume in einer Abfrage werden nicht unterstützt. Gelöschte bzw. im Papierkorb befindliche Angebote werden ausgeschlossen. Die Zeilen entsprechen der Reihenfolge in columns. - Operation-ID: get_v2_room_list_workload - Dokumentation: /docs/api/v2/operations/get-v2-room-list-workload-26a8a0208519/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | ja | Pflichtfilter: zuerst id, danach start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":true,"description":"Pflichtfilter: zuerst id, danach start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"room_012345abcdef_123456"},{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/room/list/workload' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/room/list/workload"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/room/list/workload" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_room_list_workload","summary":"Belegung für Raum abrufen","description":"Liefert eine Tabelle der Termine, die den gewünschten Zeitraum überlappen. filter benötigt start und end als parsebare Datumswerte. Auf der Listenroute muss der erste Filter property=id mit einer einzelnen öffentlichen room-ID sein; mehrere Orte/Räume in einer Abfrage werden nicht unterstützt. Gelöschte bzw. im Papierkorb befindliche Angebote werden ausgeschlossen. Die Zeilen entsprechen der Reihenfolge in columns.","tags":["room"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":true,"description":"Pflichtfilter: zuerst id, danach start und end. Die Zeitwerte werden mit PHP strtotime ausgewertet.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string"}},"additionalProperties":true}},"example":[{"property":"id","value":"room_012345abcdef_123456"},{"property":"start","value":"2026-11-01T00:00:00+01:00"},{"property":"end","value":"2026-12-01T00:00:00+01:00"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"columns":[{"field":"offername","type":"string"},{"field":"name","type":"string"},{"field":"offer_id","type":"string"},{"field":"date_id","type":"string"},{"field":"offerdef_id","type":"string"},{"field":"place_id","type":"string"},{"field":"room_id","type":"string"},{"field":"status","type":"integer"},{"field":"date_start","type":"date"},{"field":"date_end","type":"date"}],"rows":[["Beispielangebot","Einführung","offer_012345abcdef_123456","date_012345abcdef_123456","offerdef_012345abcdef_123456","place_012345abcdef_123456","room_012345abcdef_123456",1,"2026-11-03T09:00:00+01:00","2026-11-03T10:00:00+01:00"]]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"columns":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"type":{"type":"string"}},"additionalProperties":true,"required":["field","type"]}},"rows":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},{"type":"string","pattern":"^date_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["date_012345abcdef_123456"]},{"type":"string","pattern":"^offerdef_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["offerdef_012345abcdef_123456"]},{"anyOf":[{"type":"string","pattern":"^place_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["place_012345abcdef_123456"]},{"const":false}]},{"anyOf":[{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID im gewählten Konto; Beispiel ist synthetisch.","examples":["room_012345abcdef_123456"]},{"const":false}]},{"type":"integer"},{"anyOf":[{"type":"string"},{"const":false}]},{"anyOf":[{"type":"string"},{"const":false}]}],"minItems":10,"maxItems":10}}},"additionalProperties":true,"required":["columns","rows"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminfunc.php","line":172},"reason":"Workload SELECT and formatting only; Placefunc194/Roomfunc12 likewise."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/03-offers-places.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Room.php","method":"getRoom","line":157,"branch":{"file":"application/models/v2/Room.php","line":173}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"room","short":"room","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/saml SAML-Provider entfernen Markiert den konto-eigenen SAML-Provider als gelöscht. Die öffentliche Ziel-ID wählt den Provider; bestehende Kontodaten werden nicht als neuer Provider angelegt. - Operation-ID: delete_v2_saml - Dokumentation: /docs/api/v2/operations/delete-v2-saml-b41d9ec8c846/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "saml_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/saml' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "saml_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"saml_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_saml","summary":"SAML-Provider entfernen","description":"Markiert den konto-eigenen SAML-Provider als gelöscht. Die öffentliche Ziel-ID wählt den Provider; bestehende Kontodaten werden nicht als neuer Provider angelegt.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"saml_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"SAML-Provider entfernen","value":{"id":"saml_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"deleteSaml","line":389},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/saml SAML-Provider auflisten Liest die im gewählten Konto verfügbaren SAML-Provider. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft. - Operation-ID: get_v2_saml - Dokumentation: /docs/api/v2/operations/get-v2-saml-ffd41d79e539/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/saml' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_saml","summary":"SAML-Provider auflisten","description":"Liest die im gewählten Konto verfügbaren SAML-Provider. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"saml_012345abcdef_123456":{"id":"saml_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"getSaml","line":405},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/saml SAML-Provider anlegen Importiert IdP-Metadaten über remote_xml_url oder remote_xml_file (XML bzw. Base64-XML), erzeugt das SP-Zertifikat und startet deaktiviert. provider_name ist erforderlich; bei login_scope=both/admin wird eine gültige permission benötigt, bei user kann das Standardprofil dienen. Aktivierung erst nach vollständigem Feldmapping; gültiges IdP-Zertifikat mit mehr als sieben Tagen Restlaufzeit erforderlich. - Operation-ID: post_v2_saml - Dokumentation: /docs/api/v2/operations/post-v2-saml-2d3c92ce3e9c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "provider_name": "Beispiel-Identitätsanbieter", "permission": "permission_012345abcdef_123456", "status": false, "login_scope": "both", "remote_xml_url": "https://idp.example.invalid/metadata.xml" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/saml' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "provider_name": "Beispiel-Identitätsanbieter", "permission": "permission_012345abcdef_123456", "status": false, "login_scope": "both", "remote_xml_url": "https://idp.example.invalid/metadata.xml" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"provider_name\":\"Beispiel-Identitätsanbieter\",\"permission\":\"permission_012345abcdef_123456\",\"status\":false,\"login_scope\":\"both\",\"remote_xml_url\":\"https://idp.example.invalid/metadata.xml\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_saml","summary":"SAML-Provider anlegen","description":"Importiert IdP-Metadaten über remote_xml_url oder remote_xml_file (XML bzw. Base64-XML), erzeugt das SP-Zertifikat und startet deaktiviert. provider_name ist erforderlich; bei login_scope=both/admin wird eine gültige permission benötigt, bei user kann das Standardprofil dienen. Aktivierung erst nach vollständigem Feldmapping; gültiges IdP-Zertifikat mit mehr als sieben Tagen Restlaufzeit erforderlich.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"saml_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Creation must remain disabled: getter uses loose PHP == true and before-insert rejects normalized enabled state with ED80100/status_activation_after_setup, then forces 0.","description":"Creation must remain disabled: getter uses loose PHP == true and before-insert rejects normalized enabled state with ED80100/status_activation_after_setup, then forces 0.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"permission":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":50},"provider_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":63},"remote_xml_url":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":500,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":76},"remote_xml_content":{"not":{},"description":"Omit on ordinary single-record POST: getter returns strict false and API_DATA rejects the record before before-insert. The XML is obtained from remote_xml_file/remote_xml_url and its parser output.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"login_service_url":{"not":{},"description":"Omit on ordinary single-record POST: getter returns strict false and API_DATA rejects the record before before-insert. The service URL is extracted from parsed metadata.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"logout_service_url":{"not":{},"description":"Omit on ordinary single-record POST: getter returns strict false and API_DATA rejects the record before before-insert. The service URL is extracted from parsed metadata.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"field_maping":{"not":{},"description":"Omit on ordinary single-record POST: getter always returns false, causing ED80100 before the before-insert hook. Creation derives mappings from metadata instead.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"allow_migration_user":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":135,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"allow_permission_acl":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":144,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"login_scope":{"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Strings are trimmed and lowercased. Nonempty unknown strings are rejected with ED80100/invalid_login_scope. Empty/whitespace-only strings and non-string JSON values normalize to both. This is not an exact case-sensitive input enum.","description":"Strings are trimmed and lowercased. Nonempty unknown strings are rejected with ED80100/invalid_login_scope. Empty/whitespace-only strings and non-string JSON values normalize to both. This is not an exact case-sensitive input enum.","x-edoobox-recognizedValues":["both","admin","user"],"examples":[" User "],"anyOf":[{"type":"string","pattern":"^[ \\t\\n\\r\\x00\\x0B]*(?:[bB][oO][tT][hH]|[aA][dD][mM][iI][nN]|[uU][sS][eE][rR])?[ \\t\\n\\r\\x00\\x0B]*$"},{"type":["number","boolean","null","array","object"]}],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"description":"Exact OneLogin NameID format; input validator strictly rejects every other value. No trimming/case folding precedes validation.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"auth_request_signed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"POST getter uses PHP loose comparison to true. Canonical client value is boolean; non-boolean values are normalized, not rejected by a wire type validator.","description":"POST getter uses PHP loose comparison to true. Canonical client value is boolean; non-boolean values are normalized, not rejected by a wire type validator.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"disable_third_party_cookie":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"POST getter uses PHP loose comparison to true. Canonical client value is boolean; non-boolean values are normalized, not rejected by a wire type validator.","description":"POST getter uses PHP loose comparison to true. Canonical client value is boolean; non-boolean values are normalized, not rejected by a wire type validator.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"regenerate_certificate":{"not":{},"description":"POST has no supported regenerate_certificate input: Structure defines no get, so generic insertion attempts a missing callback before any field func can run. Omit it on creation.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"enforce_offer_tags":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":222,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"offer_tags_additive":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":231,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","pattern":"^[nN][oO][rR][mM][aA][lL]$","description":"Optional; missing type defaults to normal. ASCII case is normalized before validation."}},"additionalProperties":true,"required":["tag"]}},{"type":"null"}],"description":"Null or [] clears provider tags. Only participant/normal tags are supported. All final provider tags, including retained tags during an offer_filter_tags-only update, must exist in the selected account before tag/filter writes.","x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."}},{"type":"null"}],"description":"Null or [] clears offer-filter tags. Every filter tag must also be a provider tag. All final provider tags, including retained tags during an offer_filter_tags-only update, must exist in the selected account before tag/filter writes.","x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":317},"date_create":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":327},"remote_xml_file":{"type":"string","description":"Vollständiges IdP-Metadaten-XML mit XML-Deklaration, wahlweise Base64-codiert. Alternative zur remote_xml_url."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["provider_name"],"minProperties":1,"allOf":[{"anyOf":[{"required":["remote_xml_url"]},{"required":["remote_xml_file"]}]},{"anyOf":[{"required":["permission"]},{"required":["login_scope"],"properties":{"login_scope":{"type":"string","pattern":"^\\s*[uU][sS][eE][rR]\\s*$"}}}]}]},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Creation must remain disabled: getter uses loose PHP == true and before-insert rejects normalized enabled state with ED80100/status_activation_after_setup, then forces 0.","description":"Creation must remain disabled: getter uses loose PHP == true and before-insert rejects normalized enabled state with ED80100/status_activation_after_setup, then forces 0.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"permission":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":50},"provider_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":63},"remote_xml_url":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":500,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":76},"remote_xml_content":{"not":{},"description":"Omit on ordinary single-record POST: getter returns strict false and API_DATA rejects the record before before-insert. The XML is obtained from remote_xml_file/remote_xml_url and its parser output.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"login_service_url":{"not":{},"description":"Omit on ordinary single-record POST: getter returns strict false and API_DATA rejects the record before before-insert. The service URL is extracted from parsed metadata.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"logout_service_url":{"not":{},"description":"Omit on ordinary single-record POST: getter returns strict false and API_DATA rejects the record before before-insert. The service URL is extracted from parsed metadata.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"field_maping":{"not":{},"description":"Omit on ordinary single-record POST: getter always returns false, causing ED80100 before the before-insert hook. Creation derives mappings from metadata instead.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"allow_migration_user":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":135,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"allow_permission_acl":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":144,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"login_scope":{"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Strings are trimmed and lowercased. Nonempty unknown strings are rejected with ED80100/invalid_login_scope. Empty/whitespace-only strings and non-string JSON values normalize to both. This is not an exact case-sensitive input enum.","description":"Strings are trimmed and lowercased. Nonempty unknown strings are rejected with ED80100/invalid_login_scope. Empty/whitespace-only strings and non-string JSON values normalize to both. This is not an exact case-sensitive input enum.","x-edoobox-recognizedValues":["both","admin","user"],"examples":[" User "],"anyOf":[{"type":"string","pattern":"^[ \\t\\n\\r\\x00\\x0B]*(?:[bB][oO][tT][hH]|[aA][dD][mM][iI][nN]|[uU][sS][eE][rR])?[ \\t\\n\\r\\x00\\x0B]*$"},{"type":["number","boolean","null","array","object"]}],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"description":"Exact OneLogin NameID format; input validator strictly rejects every other value. No trimming/case folding precedes validation.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"auth_request_signed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"POST getter uses PHP loose comparison to true. Canonical client value is boolean; non-boolean values are normalized, not rejected by a wire type validator.","description":"POST getter uses PHP loose comparison to true. Canonical client value is boolean; non-boolean values are normalized, not rejected by a wire type validator.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"disable_third_party_cookie":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"POST getter uses PHP loose comparison to true. Canonical client value is boolean; non-boolean values are normalized, not rejected by a wire type validator.","description":"POST getter uses PHP loose comparison to true. Canonical client value is boolean; non-boolean values are normalized, not rejected by a wire type validator.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"regenerate_certificate":{"not":{},"description":"POST has no supported regenerate_certificate input: Structure defines no get, so generic insertion attempts a missing callback before any field func can run. Omit it on creation.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"enforce_offer_tags":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":222,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"offer_tags_additive":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":231,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","pattern":"^[nN][oO][rR][mM][aA][lL]$","description":"Optional; missing type defaults to normal. ASCII case is normalized before validation."}},"additionalProperties":true,"required":["tag"]}},{"type":"null"}],"description":"Null or [] clears provider tags. Only participant/normal tags are supported. All final provider tags, including retained tags during an offer_filter_tags-only update, must exist in the selected account before tag/filter writes.","x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."}},{"type":"null"}],"description":"Null or [] clears offer-filter tags. Every filter tag must also be a provider tag. All final provider tags, including retained tags during an offer_filter_tags-only update, must exist in the selected account before tag/filter writes.","x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":317},"date_create":{"x-edoobox-canonicalType":"string","description":"dP uses PHP strtotime(); unparseable/falsy results normalize to zero. ISO 8601 text is the canonical client input; no rejecting JSON date-time format assertion is established here. Account/action hooks may add preconditions.","examples":["2026-10-15T10:00:00+02:00"],"x-edoobox-writableOnUpdate":false,"x-edoobox-sourceLine":327},"remote_xml_file":{"type":"string","description":"Vollständiges IdP-Metadaten-XML mit XML-Deklaration, wahlweise Base64-codiert. Alternative zur remote_xml_url."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["provider_name"],"allOf":[{"anyOf":[{"required":["remote_xml_url"]},{"required":["remote_xml_file"]}]},{"anyOf":[{"required":["permission"]},{"required":["login_scope"],"properties":{"login_scope":{"type":"string","pattern":"^\\s*[uU][sS][eE][rR]\\s*$"}}}]}]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"SAML-Provider anlegen","value":{"data":{"provider_name":"Beispiel-Identitätsanbieter","permission":"permission_012345abcdef_123456","status":false,"login_scope":"both","remote_xml_url":"https://idp.example.invalid/metadata.xml"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"postSaml","line":349},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/saml SAML-Provider ändern Ändert Provider-Konfiguration, Login-Geltungsbereich, Feldmapping und Teilnehmer-Tags. Aktivierung prüft Metadaten und Pflichtmapping für first_name, last_name, email sowie bedingt acl. offer_filter_tags müssen Teil der normalen Provider-tags sein. regenerate_certificate=true/1/"1" ersetzt das aktive SP-Zertifikat; das erfordert einen abgeglichenen IdP. Die öffentliche Ziel-ID muss als id im JSON-Request angegeben werden. - Operation-ID: put_v2_saml - Dokumentation: /docs/api/v2/operations/put-v2-saml-0693d57c116d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "saml_012345abcdef_123456", "data": { "provider_name": "Beispiel-IDP" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/saml' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "saml_012345abcdef_123456", "data": { "provider_name": "Beispiel-IDP" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"saml_012345abcdef_123456\",\"data\":{\"provider_name\":\"Beispiel-IDP\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_saml","summary":"SAML-Provider ändern","description":"Ändert Provider-Konfiguration, Login-Geltungsbereich, Feldmapping und Teilnehmer-Tags. Aktivierung prüft Metadaten und Pflichtmapping für first_name, last_name, email sowie bedingt acl. offer_filter_tags müssen Teil der normalen Provider-tags sein. regenerate_certificate=true/1/\"1\" ersetzt das aktive SP-Zertifikat; das erfordert einen abgeglichenen IdP. Die öffentliche Ziel-ID muss als id im JSON-Request angegeben werden.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"saml_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Only literal JSON false immediately disables. Every other JSON value attempts activation, which validates provider settings, a generated login redirect URL and required field mappings.","description":"Only literal JSON false immediately disables. Every other JSON value attempts activation, which validates provider settings, a generated login redirect URL and required field mappings.","examples":[false],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"permission":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":50},"provider_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":63},"remote_xml_url":false,"remote_xml_content":{"not":{},"description":"PUT getter returns strict false; generic update throws ED80100 for remote_xml_content. This is not a metadata upload field.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"login_service_url":false,"logout_service_url":false,"field_maping":{"description":"Replaces field mappings while keeping provider idp metadata. Empty/false-like mapping is rejected. Duplicate first_name,last_name,email or acl targets are rejected; additional user-field duplicates are not checked by this loop.","examples":[[{"name":"Synthetic given name","description":"Synthetic field","attr":"givenName","field":"first_name","only_user":false}]],"x-edoobox-schemaStatus":"source-reviewed","anyOf":[{"type":"array","minItems":1,"items":{"type":"object","properties":{"name":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"description":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"attr":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"field":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Known base fields first_name,last_name,email,acl and data_1 through data_30 are retained; unknown scalar targets normalize to false. No rejecting target enum is implemented.","description":"Known base fields first_name,last_name,email,acl and data_1 through data_30 are retained; unknown scalar targets normalize to false. No rejecting target enum is implemented.","x-edoobox-recognizedValues":["first_name","last_name","email","acl","data_1..data_30"]},"only_user":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PHP loose == true normalization; data_1..data_30 force true and an unmapped target forces false.","description":"PHP loose == true normalization; data_1..data_30 force true and an unmapped target forces false."}},"additionalProperties":true,"description":"Canonical entries carry name, description, attr and field, plus optional only_user. Direct offset reads do not establish a deliberate missing-key validator; omitted keys can warn and normalize to empty text or false."}},{"type":"object","minProperties":1,"additionalProperties":{"type":"object","properties":{"name":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"description":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"attr":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"field":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Known base fields first_name,last_name,email,acl and data_1 through data_30 are retained; unknown scalar targets normalize to false. No rejecting target enum is implemented.","description":"Known base fields first_name,last_name,email,acl and data_1 through data_30 are retained; unknown scalar targets normalize to false. No rejecting target enum is implemented.","x-edoobox-recognizedValues":["first_name","last_name","email","acl","data_1..data_30"]},"only_user":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PHP loose == true normalization; data_1..data_30 force true and an unmapped target forces false.","description":"PHP loose == true normalization; data_1..data_30 force true and an unmapped target forces false."}},"additionalProperties":true,"description":"Canonical entries carry name, description, attr and field, plus optional only_user. Direct offset reads do not establish a deliberate missing-key validator; omitted keys can warn and normalize to empty text or false."}}],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"allow_migration_user":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":135,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"allow_permission_acl":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":144,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"login_scope":{"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Strings are trimmed and lowercased. Nonempty unknown strings are rejected with ED80100/invalid_login_scope. Empty/whitespace-only strings and non-string JSON values normalize to both. This is not an exact case-sensitive input enum.","description":"Strings are trimmed and lowercased. Nonempty unknown strings are rejected with ED80100/invalid_login_scope. Empty/whitespace-only strings and non-string JSON values normalize to both. This is not an exact case-sensitive input enum.","x-edoobox-recognizedValues":["both","admin","user"],"anyOf":[{"type":"string","pattern":"^[ \\t\\n\\r\\x00\\x0B]*(?:[bB][oO][tT][hH]|[aA][dD][mM][iI][nN]|[uU][sS][eE][rR])?[ \\t\\n\\r\\x00\\x0B]*$"},{"type":["number","boolean","null","array","object"]}],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"description":"Exact OneLogin NameID format; input validator strictly rejects every other value. No trimming/case folding precedes validation.","examples":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"],"x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"auth_request_signed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PUT hook enables only literal JSON true; all other JSON values disable. It uses edisset followed by === true, not a general boolean parser.","description":"PUT hook enables only literal JSON true; all other JSON values disable. It uses edisset followed by === true, not a general boolean parser.","examples":[true],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"disable_third_party_cookie":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PUT hook enables only literal JSON true; all other JSON values disable. It uses edisset followed by === true, not a general boolean parser.","description":"PUT hook enables only literal JSON true; all other JSON values disable. It uses edisset followed by === true, not a general boolean parser.","examples":[true],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"regenerate_certificate":{"enum":[true,1,"1"],"description":"PUT command accepts only true, integer 1 or string 1 by strict in_array. Rotates service-provider certificate; false is rejected.","writeOnly":true,"x-edoobox-validation":"PHP strict in_array(value, [true, 1, string 1], true); numeric token 1.0 decodes to float and is rejected.","examples":[true],"x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"enforce_offer_tags":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":222,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"offer_tags_additive":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":231,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","pattern":"^[nN][oO][rR][mM][aA][lL]$","description":"Optional; missing type defaults to normal. ASCII case is normalized before validation."}},"additionalProperties":true,"required":["tag"]}},{"type":"null"}],"description":"Null or [] clears provider tags. Only participant/normal tags are supported. All final provider tags, including retained tags during an offer_filter_tags-only update, must exist in the selected account before tag/filter writes.","x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."}},{"type":"null"}],"description":"Null or [] clears offer-filter tags. Every filter tag must also be a provider tag. All final provider tags, including retained tags during an offer_filter_tags-only update, must exist in the selected account before tag/filter writes.","x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["remote_xml_url","login_service_url","logout_service_url","date_valid","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"SAML-Provider ändern","value":{"id":"saml_012345abcdef_123456","data":{"provider_name":"Beispiel-IDP"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"updateSaml","line":373},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/saml/{id} SAML-Provider entfernen Markiert den konto-eigenen SAML-Provider als gelöscht. Die öffentliche Ziel-ID wählt den Provider; bestehende Kontodaten werden nicht als neuer Provider angelegt. - Operation-ID: delete_v2_saml_id - Dokumentation: /docs/api/v2/operations/delete-v2-saml-id-8094e0d6e428/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/saml/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_saml_id","summary":"SAML-Provider entfernen","description":"Markiert den konto-eigenen SAML-Provider als gelöscht. Die öffentliche Ziel-ID wählt den Provider; bestehende Kontodaten werden nicht als neuer Provider angelegt.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"saml_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^saml_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem gewählten Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"examples":{"request":{"summary":"SAML-Provider entfernen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"deleteSaml","line":389},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/saml/{id} SAML-Provider lesen Liest den durch die öffentliche Pfad-ID gewählten SAML-Provider. Auch ein Einzelabruf liefert die normale GET-Datenstruktur; Feldauswahl, Filter und Konto-Berechtigungen bleiben wirksam. - Operation-ID: get_v2_saml_id - Dokumentation: /docs/api/v2/operations/get-v2-saml-id-77451de13ed9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/saml/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_saml_id","summary":"SAML-Provider lesen","description":"Liest den durch die öffentliche Pfad-ID gewählten SAML-Provider. Auch ein Einzelabruf liefert die normale GET-Datenstruktur; Feldauswahl, Filter und Konto-Berechtigungen bleiben wirksam.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"saml_012345abcdef_123456":{"id":"saml_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"getSaml","line":405},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/saml/{id} SAML-Provider ändern Ändert Provider-Konfiguration, Login-Geltungsbereich, Feldmapping und Teilnehmer-Tags. Aktivierung prüft Metadaten und Pflichtmapping für first_name, last_name, email sowie bedingt acl. offer_filter_tags müssen Teil der normalen Provider-tags sein. regenerate_certificate=true/1/"1" ersetzt das aktive SP-Zertifikat; das erfordert einen abgeglichenen IdP. Die öffentliche Ziel-ID steht im Pfad. - Operation-ID: put_v2_saml_id - Dokumentation: /docs/api/v2/operations/put-v2-saml-id-c26ca99b716d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/saml/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_saml_id","summary":"SAML-Provider ändern","description":"Ändert Provider-Konfiguration, Login-Geltungsbereich, Feldmapping und Teilnehmer-Tags. Aktivierung prüft Metadaten und Pflichtmapping für first_name, last_name, email sowie bedingt acl. offer_filter_tags müssen Teil der normalen Provider-tags sein. regenerate_certificate=true/1/\"1\" ersetzt das aktive SP-Zertifikat; das erfordert einen abgeglichenen IdP. Die öffentliche Ziel-ID steht im Pfad.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"saml_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Only literal JSON false immediately disables. Every other JSON value attempts activation, which validates provider settings, a generated login redirect URL and required field mappings.","description":"Only literal JSON false immediately disables. Every other JSON value attempts activation, which validates provider settings, a generated login redirect URL and required field mappings.","examples":[false],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":50},"provider_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":150,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":63},"remote_xml_url":false,"remote_xml_content":{"not":{},"description":"PUT getter returns strict false; generic update throws ED80100 for remote_xml_content. This is not a metadata upload field.","x-edoobox-schemaStatus":"reviewed-not-writable","x-edoobox-clientRestriction":"Omit this field from client writes; the runtime behavior is described separately.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":false,"logout_service_url":false,"field_maping":{"description":"Replaces field mappings while keeping provider idp metadata. Empty/false-like mapping is rejected. Duplicate first_name,last_name,email or acl targets are rejected; additional user-field duplicates are not checked by this loop.","examples":[[{"name":"Synthetic given name","description":"Synthetic field","attr":"givenName","field":"first_name","only_user":false}]],"x-edoobox-schemaStatus":"source-reviewed","anyOf":[{"type":"array","minItems":1,"items":{"type":"object","properties":{"name":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"description":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"attr":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"field":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Known base fields first_name,last_name,email,acl and data_1 through data_30 are retained; unknown scalar targets normalize to false. No rejecting target enum is implemented.","description":"Known base fields first_name,last_name,email,acl and data_1 through data_30 are retained; unknown scalar targets normalize to false. No rejecting target enum is implemented.","x-edoobox-recognizedValues":["first_name","last_name","email","acl","data_1..data_30"]},"only_user":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PHP loose == true normalization; data_1..data_30 force true and an unmapped target forces false.","description":"PHP loose == true normalization; data_1..data_30 force true and an unmapped target forces false."}},"additionalProperties":true,"description":"Canonical entries carry name, description, attr and field, plus optional only_user. Direct offset reads do not establish a deliberate missing-key validator; omitted keys can warn and normalize to empty text or false."}},{"type":"object","minProperties":1,"additionalProperties":{"type":"object","properties":{"name":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"description":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"attr":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"aP normalizes strings and replaces every non-string JSON value with empty string.","description":"aP normalizes strings and replaces every non-string JSON value with empty string."},"field":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Known base fields first_name,last_name,email,acl and data_1 through data_30 are retained; unknown scalar targets normalize to false. No rejecting target enum is implemented.","description":"Known base fields first_name,last_name,email,acl and data_1 through data_30 are retained; unknown scalar targets normalize to false. No rejecting target enum is implemented.","x-edoobox-recognizedValues":["first_name","last_name","email","acl","data_1..data_30"]},"only_user":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PHP loose == true normalization; data_1..data_30 force true and an unmapped target forces false.","description":"PHP loose == true normalization; data_1..data_30 force true and an unmapped target forces false."}},"additionalProperties":true,"description":"Canonical entries carry name, description, attr and field, plus optional only_user. Direct offset reads do not establish a deliberate missing-key validator; omitted keys can warn and normalize to empty text or false."}}],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":135,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"allow_permission_acl":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":144,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"login_scope":{"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Strings are trimmed and lowercased. Nonempty unknown strings are rejected with ED80100/invalid_login_scope. Empty/whitespace-only strings and non-string JSON values normalize to both. This is not an exact case-sensitive input enum.","description":"Strings are trimmed and lowercased. Nonempty unknown strings are rejected with ED80100/invalid_login_scope. Empty/whitespace-only strings and non-string JSON values normalize to both. This is not an exact case-sensitive input enum.","x-edoobox-recognizedValues":["both","admin","user"],"anyOf":[{"type":"string","pattern":"^[ \\t\\n\\r\\x00\\x0B]*(?:[bB][oO][tT][hH]|[aA][dD][mM][iI][nN]|[uU][sS][eE][rR])?[ \\t\\n\\r\\x00\\x0B]*$"},{"type":["number","boolean","null","array","object"]}],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"description":"Exact OneLogin NameID format; input validator strictly rejects every other value. No trimming/case folding precedes validation.","examples":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"],"x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PUT hook enables only literal JSON true; all other JSON values disable. It uses edisset followed by === true, not a general boolean parser.","description":"PUT hook enables only literal JSON true; all other JSON values disable. It uses edisset followed by === true, not a general boolean parser.","examples":[true],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"PUT hook enables only literal JSON true; all other JSON values disable. It uses edisset followed by === true, not a general boolean parser.","description":"PUT hook enables only literal JSON true; all other JSON values disable. It uses edisset followed by === true, not a general boolean parser.","examples":[true],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"enum":[true,1,"1"],"description":"PUT command accepts only true, integer 1 or string 1 by strict in_array. Rotates service-provider certificate; false is rejected.","writeOnly":true,"x-edoobox-validation":"PHP strict in_array(value, [true, 1, string 1], true); numeric token 1.0 decodes to float and is rejected.","examples":[true],"x-edoobox-schemaStatus":"source-reviewed","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":222,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"offer_tags_additive":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":231,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."},"type":{"type":"string","pattern":"^[nN][oO][rR][mM][aA][lL]$","description":"Optional; missing type defaults to normal. ASCII case is normalized before validation."}},"additionalProperties":true,"required":["tag"]}},{"type":"null"}],"description":"Null or [] clears provider tags. Only participant/normal tags are supported. All final provider tags, including retained tags during an offer_filter_tags-only update, must exist in the selected account before tag/filter writes.","x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Public tag v2 ID returned for a currently existing record in the selected account; signature, resource type and account ownership are checked by the native hook."}},{"type":"null"}],"description":"Null or [] clears offer-filter tags. Every filter tag must also be a provider tag. All final provider tags, including retained tags during an offer_filter_tags-only update, must exist in the selected account before tag/filter writes.","x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["remote_xml_url","login_service_url","logout_service_url","date_valid","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"status":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"updateSaml","line":373},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/saml/{id}/federationmetadata.xml SP-Metadaten für einen SAML-Provider herunterladen Liefert application/xml mit den aktuellen öffentlichen Service-Provider-Metadaten und dem aktiven SP-Zertifikat des konto-eigenen Providers. Die öffentliche saml-Pfad-ID ist erforderlich; private Zertifikatschlüssel werden nicht veröffentlicht. Kein neuer Provider und kein neues Zertifikat werden angelegt. - Operation-ID: get_v2_saml_id_federationmetadata_xml - Dokumentation: /docs/api/v2/operations/get-v2-saml-id-federationmetadata-xml-7ba89282ea00/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/saml/'"${PATH_ID}"'/federationmetadata.xml' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml/{id}/federationmetadata.xml"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml/{id}/federationmetadata.xml" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_saml_id_federationmetadata_xml","summary":"SP-Metadaten für einen SAML-Provider herunterladen","description":"Liefert application/xml mit den aktuellen öffentlichen Service-Provider-Metadaten und dem aktiven SP-Zertifikat des konto-eigenen Providers. Die öffentliche saml-Pfad-ID ist erforderlich; private Zertifikatschlüssel werden nicht veröffentlicht. Kein neuer Provider und kein neues Zertifikat werden angelegt.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"SP-Federationsmetadaten als XML; bei fehlendem Provider/Zertifikat normaler JSON-Fehler.","content":{"application/xml":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Samlfunc.php","line":1167},"reason":"Reads existing SAML configuration and emits XML; no certificate generation in this path."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"getSaml","line":405,"branch":{"file":"application/models/v2/Saml.php","line":416}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/saml/list SAML-Provider auflisten Liest die im gewählten Konto verfügbaren SAML-Provider. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft. - Operation-ID: get_v2_saml_list - Dokumentation: /docs/api/v2/operations/get-v2-saml-list-451b679b784c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/saml/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_saml_list","summary":"SAML-Provider auflisten","description":"Liest die im gewählten Konto verfügbaren SAML-Provider. Feldauswahl, Filter, Sortierung und Pagination folgen dem Ressourcenvertrag. Konto-Kontext und aktuelle Berechtigungen werden geprüft.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"saml_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"saml_012345abcdef_123456":{"id":"saml_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["saml_012345abcdef_123456"]},"status":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":40,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":224,"reason":"Create forbids activating before setup and forces initial disabled status.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1274,"reason":"Actual activation hook: strict false short circuit, settings/URL and required mapping checks.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Provider config depends on stored certificate/mapping and may refresh metadata XML in real runtime.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1357,"reason":"Metadata refresh delegates parsing and may fetch XML during real runtime; not invoked by this source review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":85,"reason":"Login URL wrapper delegates to OneLogin auth login with stay=true.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Required mapping keys are explicit.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":246,"reason":"Followed injectSettings/getSettings: build SP/IdP settings and instantiate OneLogin Auth, with signing and stored certificate settings.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":115,"reason":"Followed service-provider settings construction used for activation.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":174,"reason":"Installed OneLogin Auth delegates validation to its Settings constructor.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"vendor/onelogin/php-saml/src/Saml2/Auth.php","line":542,"reason":"Installed OneLogin login builds the SAML request and a redirect URL; this is not a completed external provider handshake.","sha256":"d4a3737528426ace71044fbc90e1a06273a76149c55ee1410dc21ae225ed4a8a"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"permission":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":50,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["permission_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"provider_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":63,"type":"string"},"remote_xml_url":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":76,"type":"string"},"remote_xml_content":{"type":"string","description":"Metadata XML content stored by Samlfunc; setter passes it through verbatim.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":90,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":236,"reason":"Creation reads remote_xml_file or remote_xml_url, validates/parses XML and assigns escaped metadata content.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"login_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":103,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"logout_service_url":{"type":"string","readOnly":true,"description":"Decoded metadata service URL text; logout may be empty.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":113,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":297,"reason":"Before-insert derives SSO/SLO URLs; missing valid SSO rejects provider, optional SLO can remain false.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":232,"reason":"Followed XML parsing wrapper to the installed OneLogin metadata parser.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php","line":115,"reason":"Installed parser loads XML, selects entity descriptor and extracts SSO/SLO locations and bindings.","sha256":"4df0c1b7bf79d2df3d1903099fde2edaefe07b7d690f427958a0746f3daf6e9e"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"field_maping":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"attr":{"type":"string"},"field":{"anyOf":[{"type":"string"},{"const":false}]},"only_user":{"type":"boolean"}},"required":["name","description","attr","field","only_user"],"additionalProperties":true},"description":"Normal output is the fields array decoded from stored mapping; manually malformed legacy JSON is not repaired by the setter.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":123,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2861,"reason":"Actual PUT mapping loop, truthiness requirement, duplicate detection and full replacement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":480,"reason":"Followed target normalizer and additional-user-field classifier.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2925,"reason":"Followed base required mapping keys.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":319,"reason":"Create mapping starts with idp metadata and empty field list.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":389,"reason":"Create mapping is derived from offered/default claims and stored, overwriting submitted batch value.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1399,"reason":"Followed getProviderData: existing provider/certificate lookup and mapping metadata; refresh may load remote XML in actual runtime, never during this review.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"allow_migration_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"type":"boolean"},"allow_permission_acl":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":144,"type":"boolean"},"login_scope":{"type":"string","enum":["both","admin","user"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":153,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":22,"reason":"Followed validateProvidedLoginScope and provider normalizer.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":197,"reason":"Before-insert sets scope and applies dependent permission requirement.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2840,"reason":"Actual PUT hook delegates to scope validator.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":42,"reason":"Allowed normalized scopes and default both; output normalizer.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"name_id_format":{"type":"string","enum":["urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified","urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress","urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"],"x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":172,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2770,"reason":"PUT checks exact NameID allowed values before aP storage.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/libraries/edoobox/integrations/auth/Saml.php","line":14,"reason":"Strict allowed formats and separate response fallback.","sha256":"cb122e325068b8432675dd52856fd095233782d10652ee3a56ee8161ff3acb57"},{"file":"vendor/onelogin/php-saml/src/Saml2/Constants.php","line":26,"reason":"Followed installed dependency constants to actual standard URI strings.","sha256":"a7f6ab35d1209e34d499662ae1f8f5bedf0ae6f08325388e8b186ea85f6c236d"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"auth_request_signed":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":190,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2801,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"disable_third_party_cookie":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":200,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2810,"reason":"Actual flag hook clears provider cache and compares edisset value strictly with true.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"regenerate_certificate":{"const":false,"description":"Structure set returns false; select=false. This is an action command, not persisted public certificate material.","x-edoobox-contractSource":[{"file":"application/models/v2/Saml.php","line":210,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2819,"reason":"Strict command allowlist and generation/storage delegation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":1087,"reason":"Followed certificate payload generation and replace-active storage behavior.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":349,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":1077,"reason":"Followed certificate host resolution: base URL host is required; a bad host throws RuntimeException.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"enforce_offer_tags":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":222,"type":"boolean"},"offer_tags_additive":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":231,"type":"boolean"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"const":"normal"}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2610,"reason":"Array or null accepted; each item must contain a public tag ID.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2630,"reason":"Missing type defaults to normal; supplied type is lowercased before validation.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":256,"reason":"Response is an array of {tag,type:normal}.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"offer_filter_tags":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Samlfunc.php","line":2653,"reason":"Array or null accepted; items decode as public tag IDs.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Samlfunc.php","line":2678,"reason":"Filter tags must be contained in provider tags.","sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8"},{"file":"application/models/v2/Saml.php","line":298,"reason":"Response is an array of public tag IDs.","sha256":"d96a3b34fc395f517b58f0fe921ceffba9dd675c237f546d91cebc600ea783fc"},{"file":"application/models/v2/Samlfunc.php","line":2686,"lineEnd":2697,"sha256":"60b2704bbe30a36725ed0a56f351e4ba6f6243b3c55393b51140fbd18ae3f0d8","reason":"The final provider tag set, including retained tags on filter-only updates, must exist in this account before any tag/filter persistence."}]},"date_valid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":317,"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":327,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"getSaml","line":405},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/saml/list/no_data Leeren SAML-Aktionszweig aufrufen Legacy-Alias no_data beendet den Modellzweig ohne Provider anzulegen oder Daten zu ändern. Die normale Authentifizierung und Ressourcenberechtigung laufen weiterhin. Es ist kein Import- oder Test-Handshake. - Operation-ID: post_v2_saml_list_no_data - Dokumentation: /docs/api/v2/operations/post-v2-saml-list-no-data-66d2f2ee008c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/saml/list/no_data' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/saml/list/no_data"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "POST", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/saml/list/no_data" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_saml_list_no_data","summary":"Leeren SAML-Aktionszweig aufrufen","description":"Legacy-Alias no_data beendet den Modellzweig ohne Provider anzulegen oder Daten zu ändern. Die normale Authentifizierung und Ressourcenberechtigung laufen weiterhin. Es ist kein Import- oder Test-Handshake.","tags":["saml"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Der Modellzweig liefert keinen fachlichen Payload; die generische Dispatcher-Ausgabe bleibt maßgeblich.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"saml_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Action-dependent data; deliberately unconstrained until a complete field contract is established."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/05-auth.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Saml.php","method":"postSaml","line":349,"branch":{"file":"application/models/v2/Saml.php","line":356}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"saml","short":"saml","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/securefile Dateimetadaten auflisten Liest dieselben aktiven Dateimetadaten wie publicfile. Die Ressource nutzt publicfile-IDs; der Name securefile bezeichnet hier keinen getrennten Speicher und liefert keine signierte Download-URL. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_securefile - Dokumentation: /docs/api/v2/operations/get-v2-securefile-6fd102071b7d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/securefile' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/securefile"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/securefile" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_securefile","summary":"Dateimetadaten auflisten","description":"Liest dieselben aktiven Dateimetadaten wie publicfile. Die Ressource nutzt publicfile-IDs; der Name securefile bezeichnet hier keinen getrennten Speicher und liefert keine signierte Download-URL. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["securefile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"publicfile_012345abcdef_123456":{"id":"publicfile_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":24,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":37,"type":"string"},"file_extension":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"file_type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":24,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":37,"type":"string"},"file_extension":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"file_type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Securefile.php","method":"getSecurefile","line":170},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"securefile","short":"sefi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/securefile Dateimetadaten ändern Ändert den Anzeigenamen eines vorhandenen publicfile-Datensatzes. Es gilt die öffentliche publicfile-ID; weitere Dateimetadaten und Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_securefile - Dokumentation: /docs/api/v2/operations/put-v2-securefile-72ce6fc662f2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "file_name": "Unterlagen.pdf" }, "id": "publicfile_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/securefile' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/securefile"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "file_name": "Unterlagen.pdf" }, "id": "publicfile_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/securefile" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"file_name\":\"Unterlagen.pdf\"},\"id\":\"publicfile_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_securefile","summary":"Dateimetadaten ändern","description":"Ändert den Anzeigenamen eines vorhandenen publicfile-Datensatzes. Es gilt die öffentliche publicfile-ID; weitere Dateimetadaten und Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["securefile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"publicfile_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":24},"file_name_cdn":false,"file_extension":false,"file_type":false,"file_size":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["file_name_cdn","file_extension","file_type","file_size","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Dateimetadaten ändern","value":{"data":{"file_name":"Unterlagen.pdf"},"id":"publicfile_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Securefile.php","method":"updateSecurefile","line":137},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"securefile","short":"sefi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/securefile/{id} Dateimetadaten lesen Liest dieselben aktiven Dateimetadaten wie publicfile. Die Ressource nutzt publicfile-IDs; der Name securefile bezeichnet hier keinen getrennten Speicher und liefert keine signierte Download-URL. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_securefile_id - Dokumentation: /docs/api/v2/operations/get-v2-securefile-id-a09b48f44ef4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/securefile/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/securefile/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/securefile/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_securefile_id","summary":"Dateimetadaten lesen","description":"Liest dieselben aktiven Dateimetadaten wie publicfile. Die Ressource nutzt publicfile-IDs; der Name securefile bezeichnet hier keinen getrennten Speicher und liefert keine signierte Download-URL. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["securefile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"publicfile_012345abcdef_123456":{"id":"publicfile_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":24,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":37,"type":"string"},"file_extension":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"file_type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":24,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":37,"type":"string"},"file_extension":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"file_type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Securefile.php","method":"getSecurefile","line":170},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"securefile","short":"sefi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/securefile/{id} Dateimetadaten ändern Ändert den Anzeigenamen eines vorhandenen publicfile-Datensatzes. Es gilt die öffentliche publicfile-ID; weitere Dateimetadaten und Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_securefile_id - Dokumentation: /docs/api/v2/operations/put-v2-securefile-id-98e0e3da983f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche v2-ID des ausgewählten Objekts. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "file_name": "Unterlagen.pdf" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/securefile/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/securefile/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "file_name": "Unterlagen.pdf" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/securefile/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"file_name\":\"Unterlagen.pdf\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_securefile_id","summary":"Dateimetadaten ändern","description":"Ändert den Anzeigenamen eines vorhandenen publicfile-Datensatzes. Es gilt die öffentliche publicfile-ID; weitere Dateimetadaten und Datei-Bytes werden hier nicht ersetzt. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["securefile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche v2-ID des ausgewählten Objekts.","schema":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"publicfile_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["publicfile_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"file_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":24},"file_name_cdn":false,"file_extension":false,"file_type":false,"file_size":false,"date_create":false},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["file_name_cdn","file_extension","file_type","file_size","date_create"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["securefile_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dateimetadaten ändern","value":{"data":{"file_name":"Unterlagen.pdf"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Securefile.php","method":"updateSecurefile","line":137},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"securefile","short":"sefi","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/securefile/list Dateimetadaten auflisten Liest dieselben aktiven Dateimetadaten wie publicfile. Die Ressource nutzt publicfile-IDs; der Name securefile bezeichnet hier keinen getrennten Speicher und liefert keine signierte Download-URL. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. - Operation-ID: get_v2_securefile_list - Dokumentation: /docs/api/v2/operations/get-v2-securefile-list-ad3bab0c036c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/securefile/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/securefile/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/securefile/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_securefile_list","summary":"Dateimetadaten auflisten","description":"Liest dieselben aktiven Dateimetadaten wie publicfile. Die Ressource nutzt publicfile-IDs; der Name securefile bezeichnet hier keinen getrennten Speicher und liefert keine signierte Download-URL. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste.","tags":["securefile"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"securefile_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"publicfile_012345abcdef_123456":{"id":"publicfile_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":24,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":37,"type":"string"},"file_extension":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"file_type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^publicfile_[a-f0-9]{12}_-?[0-9]+$","examples":["publicfile_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"file_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":24,"type":"string"},"file_name_cdn":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":37,"type":"string"},"file_extension":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":51,"type":"string"},"file_type":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"string"},"file_size":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":79,"type":"integer"},"date_create":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":93,"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Securefile.php","method":"getSecurefile","line":170},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"securefile","short":"sefi","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/sendemail Nachricht an Teilnehmer, Angebote oder Administratoren senden Übernimmt Betreff und HTML in den Postausgang. provider, receivers, subject und html müssen vorhanden und nicht leer sein. receivers enthält 1 bis 50 unterschiedliche gültige user_-, offer_- oder admin_-IDs; Angebote werden auf bestätigte Teilnehmer erweitert, Wartelisteneinträge ausgelassen. E-Mail-Adressen werden dedupliziert; ungültige Empfänger erscheinen in mail_error. provider=default unterliegt der Tagesgrenze von 150 E-Mails, einschließlich der aufgelösten Angebotsteilnehmer. Optionales offer liefert Platzhalterkontext. Die Antwort wird vor dem anschließenden Einreihen der Nachrichten gesendet; mail_send ist die Anzahl aufgelöster Adressen und keine Zustellbestätigung. - Operation-ID: post_v2_sendemail - Dokumentation: /docs/api/v2/operations/post-v2-sendemail-e7e1bb46534c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "provider": "default", "receivers": [ "user_012345abcdef_123456" ], "subject": "Information zu Ihrem Kurs", "html": "

Herzlich willkommen.

", "offer": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/sendemail' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/sendemail"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "provider": "default", "receivers": [ "user_012345abcdef_123456" ], "subject": "Information zu Ihrem Kurs", "html": "

Herzlich willkommen.

", "offer": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/sendemail" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"provider\":\"default\",\"receivers\":[\"user_012345abcdef_123456\"],\"subject\":\"Information zu Ihrem Kurs\",\"html\":\"

Herzlich willkommen.

\",\"offer\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_sendemail","summary":"Nachricht an Teilnehmer, Angebote oder Administratoren senden","description":"Übernimmt Betreff und HTML in den Postausgang. provider, receivers, subject und html müssen vorhanden und nicht leer sein. receivers enthält 1 bis 50 unterschiedliche gültige user_-, offer_- oder admin_-IDs; Angebote werden auf bestätigte Teilnehmer erweitert, Wartelisteneinträge ausgelassen. E-Mail-Adressen werden dedupliziert; ungültige Empfänger erscheinen in mail_error. provider=default unterliegt der Tagesgrenze von 150 E-Mails, einschließlich der aufgelösten Angebotsteilnehmer. Optionales offer liefert Platzhalterkontext. Die Antwort wird vor dem anschließenden Einreihen der Nachrichten gesendet; mail_send ist die Anzahl aufgelöster Adressen und keine Zustellbestätigung.","tags":["sendemail"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"mail_send zählt aufgelöste Adressen; mail_error ist false oder eine Liste nicht verwendbarer Empfänger mit öffentlichen IDs.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"mail_send":1,"mail_error":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"mail_send":{"type":"integer"},"mail_error":{"anyOf":[{"const":false},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true}}]}},"additionalProperties":true,"required":["mail_send","mail_error"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"provider":{"type":"string","description":"default aktiviert das kostenlose Versandlimit; weitere Werte werden hier nicht als feste Providerliste validiert. Die tatsächliche Versandkonfiguration kommt aus dem Konto."},"receivers":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}]},"minItems":1,"description":"1 bis 50 gültige, deduplizierte Ressourcenauswahlen; ein Angebot kann mehrere Adressen ergeben."},"subject":{"type":"string"},"html":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["provider","receivers","subject","html"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Nachricht an Teilnehmer, Angebote oder Administratoren senden","value":{"data":{"provider":"default","receivers":["user_012345abcdef_123456"],"subject":"Information zu Ihrem Kurs","html":"

Herzlich willkommen.

","offer":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Sendemail.php","method":"postSendemail","line":27},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"sendemail","short":"seem","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings Account-Einstellungen abrufen Liest die zentralen Einstellungen des ausgewählten Accounts sowie Sprachen, Archiv-/Papierkorb-IDs, Währungen, Abonnement- und Versionsinformationen. Die Antwort reduziert die generische Auswahl auf das erste gefundene Einstellungsobjekt und ergänzt Account-Metadaten. Bei Bedarf werden Versions-Caches für Admin-Konfigurationsvorlagen und Mehrwertsteuer befüllt. Keine Zugangsdaten aus der Antwort veröffentlichen. - Operation-ID: get_v2_settings - Dokumentation: /docs/api/v2/operations/get-v2-settings-fec1627e5c2a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings","summary":"Account-Einstellungen abrufen","description":"Liest die zentralen Einstellungen des ausgewählten Accounts sowie Sprachen, Archiv-/Papierkorb-IDs, Währungen, Abonnement- und Versionsinformationen. Die Antwort reduziert die generische Auswahl auf das erste gefundene Einstellungsobjekt und ergänzt Account-Metadaten. Bei Bedarf werden Versions-Caches für Admin-Konfigurationsvorlagen und Mehrwertsteuer befüllt. Keine Zugangsdaten aus der Antwort veröffentlichen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Ein einzelnes Einstellungsobjekt mit zusätzlichen Account-Metadaten, auch bei /list.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","organization_name":"Beispielorganisation","organization_language":"de","root_archive":"category_012345abcdef_123456","root_trash":"category_012345abcdef_123456","language":{"languages":["Deutsch","English"],"active":["de"],"inactive":["en"]},"user_currencys":["CHF"],"account_billing_recipient_admin_id":false},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"firstweekday":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"integer"},"format_date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":53,"type":"integer"},"format_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"type":"integer"},"format_calendar_grid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":71,"type":"integer"},"format_timezone":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":5,"reason":"PUT consumes a zone-name string instead of the integer get placeholder.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":85,"reason":"Response returns the account timezone zone_name.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"congress_module":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"boolean"},"format_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"root_category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":130,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["rootcategory_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"type":"string"},"organization_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":150,"type":"string"},"organization_contactperson":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"organization_street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168,"type":"string"},"organization_postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":177,"type":"string"},"organization_town":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":186,"type":"string"},"organization_country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":195,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["organizationcountry_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"organization_language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":205,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/langedoobox_helper.php","line":731,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangIDByName returns configured entry, with fallback for unknown names."},{"file":"application/helpers/langedoobox_helper.php","line":566,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangById converts internal entries back to public language keys."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":214,"type":"string"},"organization_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":223,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP text storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_email":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":57,"reason":"PUT callback validates the trimmed email syntax, checks raw and encoded lengths, and requires an exact aP/aQ identity before returning storage. False is rejected by generic update as ED90100 before a write.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","lineEnd":75},{"file":"application/models/v2/Settings.php","line":237,"reason":"Incoming model getter is bounded to200 characters; generic PUT uses the dedicated validation hook; response unescapes stored text.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","lineEnd":246},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":247,"type":"string"},"billingaddress_organization":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":257,"type":"string"},"billingaddress_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":266,"type":"string"},"billingaddress_street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"billingaddress_postcode_town":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":284,"type":"string"},"billingaddress_phone":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":293,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Actual aP string-only/truncating storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"billingaddress_email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":303,"type":"string"},"billingaddress_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":312,"type":"boolean"},"user_duplicate_config":{"type":"object","required":["version","enabled","candidate_threshold","auto_merge_threshold","survivor_strategy","max_merge_users","rules","rules_hash","matching_rules_hash"],"properties":{"version":{"const":2},"enabled":{"type":"boolean"},"candidate_threshold":{"type":"integer","minimum":0,"maximum":100},"auto_merge_threshold":{"type":["integer","null"],"minimum":1,"maximum":100},"survivor_strategy":{"enum":["newest","oldest"]},"max_merge_users":{"type":"integer","minimum":2,"maximum":10},"rules":{"type":"array","items":{"type":"object","required":["field","enabled","required","weight"],"properties":{"field":{"enum":["gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"enabled":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer","minimum":1,"maximum":10}},"additionalProperties":false}},"rules_hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"matching_rules_hash":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":321,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Userduplicatefunc.php","line":28,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Actual normalizeConfigForSave preserves unchanged rule hashes and normalizes current state."},{"file":"application/models/v2/Userduplicatefunc.php","line":40,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Full known config defaults, coercion, clamping, hash/version semantics."},{"file":"application/models/v2/Userduplicatefunc.php","line":648,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Rule filtering, uniqueness and weight normalization."},{"file":"application/models/v2/Userduplicatefunc.php","line":968,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Allowed public data-field selectors for duplicate matching."},{"file":"application/models/v2/Userduplicatefunc.php","line":488,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Changed matching rules invalidate derived duplicate data, not only the stored JSON."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"admin_avatar_mode":{"enum":["face","symbol"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":344,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_recipient_type":{"enum":["organization","billing_address","admin"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":363,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_recipient_email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":390,"type":"string"},"account_billing_recipient_admin_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_(?:-35|[1-9][0-9]*)$","examples":["admin_012345abcdef_123456"],"description":"Canonical public admin v2 ID issued in this account; server validates the account-bound checksum and rejects raw IDs."},{"const":false,"x-edoobox-id-kind":"empty-relation"}],"description":"Billing administrator as a public admin v2 ID; false when unset. Internal database primary keys are never serialized.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":421,"reason":"The corrected inbound getter accepts a canonical tenant-bound admin v2 reference and normalizes explicit clear sentinels; positive raw primary keys are rejected.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":437,"reason":"The outbound setter encodes a positive database admin key as a public v2 ID and emits false for zero.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/API_DATA.php","line":1853,"reason":"Join updates call the field getter before persisting; response serialization calls the field setter at line1068. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"account_billing_auto_charge_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":440,"type":"boolean"},"account_billing_topup_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":459,"type":"boolean"},"account_billing_topup_threshold":{"type":"number","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":478,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_topup_amount":{"type":"number","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"internal_all":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":516,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":23,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"notification_minimummaximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":526,"type":"boolean"},"notification_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":535,"type":"boolean"},"vat_included":{"enum":["exclusive","including"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":544,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"vat_reverse_charge_texts":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":553,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Each recognized text is normalized by aP."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_sort":{"enum":["date","manual"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":580,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_func_1":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":589,"type":"boolean"},"offer_func_1_status":{"enum":[3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":598,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_archiving":{"enum":[1,2,3,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":607,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_archiving_reference_date":{"enum":["start","visibility"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":634,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"news_and_tricks":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":644,"type":"boolean"},"booking_create_admin":{"enum":[1,2,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":653,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_userlogin":{"enum":[1,2,3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":686,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_login":{"enum":[1,2,3,null],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":713,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_login_global_users":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":738,"type":"boolean"},"booking_cancellation":{"enum":[1,2,3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":757,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cancellation_admin_confirmation_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":784,"type":"boolean"},"booking_reservation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":803,"type":"boolean"},"booking_paymentpage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":812,"type":"boolean"},"booking_sendinfo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":822,"type":"boolean"},"booking_label_notifications":{"type":"array","items":{"type":"object","required":["label","email","emailtemplate"],"properties":{"label":{"type":"string","minLength":1,"maxLength":15},"email":{"type":"string"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$"}},"additionalProperties":false},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":831,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3060,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Actual rule filtering and per-account template lookup."},{"file":"application/models/v2/Settings.php","line":3105,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Response filter emits typed public emailtemplate IDs."},{"file":"application/models/v2/Settings.php","line":3159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Template must belong to the account and be an email template, not PDF."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_send_pdf":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":850,"type":"boolean"},"booking_send_ics":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":859,"type":"boolean"},"booking_reminder":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":878,"type":"boolean"},"booking_reminder_hours":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":887,"type":"integer"},"booking_visitconfirmation":{"enum":[1,2,false]},"booking_number":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":937,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":288,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual hook range check and tenant-scoped uniqueness query."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_percent":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":948,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_notvisitmail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":975,"type":"boolean"},"booking_cert_senddelayhours":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":996,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_validation_url":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1024,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":40,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_attachpdf":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1046,"type":"boolean"},"booking_cert_attachpdfpercent":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1206,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_secondemail":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1094,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":641,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1115,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_secondemail":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1150,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":693,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1171,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_offerlist_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1233,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_offerlist_cancel_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1268,"type":"boolean"},"booking_allow_promotions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1288,"type":"boolean"},"booking_designation_promotions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1297,"type":"string"},"skip_counter":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1306,"type":"integer"},"question_deliverables_show":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1316,"type":"boolean"},"multi_offer_show":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1336,"type":"boolean"},"multi_offer_child_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1346,"type":"integer"},"automatic_deletion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1367},"edsupport_access":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1395,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"payment_confirmation_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1428,"type":"boolean"},"barcode_type":{"enum":["pdf417","qrcode","datamatrix"]},"barcode_mobile_wallet":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1461,"type":"boolean"},"mobile_wallet_background_color":{"type":"string","pattern":"^#[0-9A-F]{6}$","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1470,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_foreground_color":{"type":"string","pattern":"^(?:#[0-9A-F]{6})?$","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1479,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_use_offer_image":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1488,"type":"boolean"},"mobile_wallet_fallback_image_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_display_name":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1506,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_support_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1515,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"barcode_ticket":{"enum":[false,"always","offer"]},"collective_booking_max":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1548,"type":"integer"},"collective_booking_identify":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1563,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"collective_booking_identify_privacy":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1586,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"collective_booking_identify_obligatory":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1610,"type":"boolean"},"collective_booking_master":{"enum":["manual","all",false]},"security_headers_csp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1654,"type":"boolean"},"design_subcategory":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1699,"type":"boolean"},"legaltext_atc":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1708,"type":"boolean"},"legaltext_ri":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1717,"type":"boolean"},"legaltext_pp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1726,"type":"boolean"},"attendance_config":{"type":"array","items":{"type":"object","properties":{"status":{"type":["integer","string"],"description":"The key is derived with an integer cast; stored status representation is preserved in the record."},"order":{"type":"integer"},"counter":{},"val":{},"color":{},"help":{},"lock":{"type":"boolean"}},"additionalProperties":true,"required":["status","order"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":153,"reason":"PUT sorts the status-record array, casts status/order and normalizes counter.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settingsfunc.php","line":208,"reason":"Exactly three required-status occurrences are checked.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":1739,"reason":"Response returns ordered status records as a JSON array.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"language":{"type":"object","properties":{"languages":{"type":"array","items":{"type":"string"}},"active":{"type":["object","array"]},"inactive":{"type":["object","array"]}},"additionalProperties":true},"edsubscription":{"type":"string","pattern":"^edsubscription_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"edsubscription_testinguntildate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1786},"versions":{"type":"object","properties":{},"additionalProperties":true},"pdf_engine":{"enum":[1,2,null],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1821,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"pdf_edit_restriction_password":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1851,"type":"boolean"},"pdf_paper_size":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1870,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"vendor/dompdf/dompdf/src/Adapter/CPDF.php","line":42,"sha256":"7dca4a096b08c006f12bfccd733068223d92e502b9a625c49bd919373f714001","reason":"Installed Dompdf paper-size key list, matched case-insensitively by the source getter."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Normal fields check strict false; joined fields instead collect getter output at 1793 and execute it at 1827 without that rejection. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"pdf_custom_css":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1892,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/libraries/edoobox/integrations/pdf/Pdf.php","line":16,"sha256":"b286cdc4d20e02601db9acc35b06564357f10378961e72cb8f4e22e64b3a3254","reason":"Allowed PDF font keys used by the CSS normalizer."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"dinaco_client_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1960,"type":"string"},"dinaco_client_secret":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1979,"x-edoobox-sensitive":true},"gtm_mess_id":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1998,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP normalization after the substring test."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"gtm_container_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2024,"type":"string"},"meta_html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2044,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"system_attachments_file_naming":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{},"additionalProperties":{"type":"string"}}},{"type":"array","maxItems":0}],"description":"Associative map, or [] when there are no naming rules.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2076,"reason":"Input iterates template/ics keys and language-to-name objects; edid occurs on map keys.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":2106,"reason":"Response maps stored keys back to public emailtemplate IDs or ics.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"website_integration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2132},"website_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Strict false getter rejects invalid URL inputs. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"admin_custom_permissions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2172,"type":"boolean"},"admin_academic_title_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2192,"type":"boolean"},"root_archive":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"root_trash":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"user_currencys":{"type":"array","items":{"type":"string"}},"stripe_public_key":{"type":["string","boolean"]}},"additionalProperties":true}],"description":"Einzelnes Account-Einstellungsobjekt; auch die Listenroute reduziert auf das erste gefundene Objekt."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-sensitiveResponseFields":["dinaco_client_secret"],"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings object/dashboard query; sensitive field exposure is separately controlled."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings Account-Einstellungen ändern Aktualisiert die übergebenen zentralen Account-Einstellungen. Es werden nur bekannte, schreibbare Structure-Felder verarbeitet; Datums-/Zahlen-/Schalterwerte werden gemäß dem jeweiligen Feld normalisiert. Dynamische Datenfeldnamen für Mehrfachanmeldung oder Zweitemail müssen zur aktuellen Teilnehmerdatenstruktur gehören. Sprach-, Präsenz-, Abrechnungs- und Buchungsregeln können weitere native Validierungen und Folgeänderungen auslösen; Abonnement-/Berechtigungsfreigaben bleiben maßgeblich. - Operation-ID: put_v2_settings - Dokumentation: /docs/api/v2/operations/put-v2-settings-92f4394b8018/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "organization_name": "Beispielorganisation", "firstweekday": 1, "automatic_deletion": 180 }, "id": "account_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "organization_name": "Beispielorganisation", "firstweekday": 1, "automatic_deletion": 180 }, "id": "account_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"organization_name\":\"Beispielorganisation\",\"firstweekday\":1,\"automatic_deletion\":180},\"id\":\"account_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings","summary":"Account-Einstellungen ändern","description":"Aktualisiert die übergebenen zentralen Account-Einstellungen. Es werden nur bekannte, schreibbare Structure-Felder verarbeitet; Datums-/Zahlen-/Schalterwerte werden gemäß dem jeweiligen Feld normalisiert. Dynamische Datenfeldnamen für Mehrfachanmeldung oder Zweitemail müssen zur aktuellen Teilnehmerdatenstruktur gehören. Sprach-, Präsenz-, Abrechnungs- und Buchungsregeln können weitere native Validierungen und Folgeänderungen auslösen; Abonnement-/Berechtigungsfreigaben bleiben maßgeblich.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Account-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"firstweekday":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":44,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"format_date":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":53,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"format_time":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":62,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"format_calendar_grid":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":71,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"format_timezone":{"type":"string","description":"Existing IANA timezone name, for example Europe/Zurich; the update callback looks up zone_name.","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":5,"reason":"PUT consumes a zone-name string instead of the integer get placeholder.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":85,"reason":"Response returns the account timezone zone_name.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"congress_module":false,"format_number":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":112,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":121},"root_category":false,"shortcut":false,"organization_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":150},"organization_contactperson":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":159},"organization_street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":168},"organization_postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":177},"organization_town":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":186},"organization_country":false,"organization_language":{"description":"Maps a configured language key to its internal language entry; unknown keys default internally. Response returns the configured language key or null for an unmatched legacy value. This is a language selector, not an externally exposed numeric ID.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["de"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":205,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/langedoobox_helper.php","line":731,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangIDByName returns configured entry, with fallback for unknown names."},{"file":"application/helpers/langedoobox_helper.php","line":566,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangById converts internal entries back to public language keys."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_phone":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":214},"organization_url":{"description":"Removes exact lowercase leading http:// or https:// before aP trim/newline/truncation to 250 stored characters. No URL syntax is required; other schemes and uppercase prefixes are not normalized in that step.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["https://example.invalid"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":223,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP text storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_email":{"type":"string","maxLength":200,"description":"Trimmed input must pass PHP FILTER_VALIDATE_EMAIL. Native PUT rejects raw input over200 UTF-8 characters (including outer whitespace), escaped trimmed storage over200 characters, or any aP/aQ roundtrip change to the validated address. Rejection returns the existing invalid-email path before persistence; valid addresses are preserved.","x-edoobox-validation":"PHP FILTER_VALIDATE_EMAIL after trim; raw UTF-8 length<=200; escaped trimmed length<=200; aQ(aP(trimmed,true,200))===trimmed","x-edoobox-maxStoredLength":200,"x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":57,"reason":"PUT callback validates the trimmed email syntax, checks raw and encoded lengths, and requires an exact aP/aQ identity before returning storage. False is rejected by generic update as ED90100 before a write.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","lineEnd":75},{"file":"application/models/v2/Settings.php","line":237,"reason":"Incoming model getter is bounded to200 characters; generic PUT uses the dedicated validation hook; response unescapes stored text.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","lineEnd":246},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_currency":false,"billingaddress_organization":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":257},"billingaddress_user":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":50,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":266},"billingaddress_street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":275},"billingaddress_postcode_town":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":284},"billingaddress_phone":{"description":"Trims the value then applies aP text normalization (trim/newline replacement/truncation to 250 stored characters). It does not validate telephone syntax. Some nonscalar values cause a PHP type error before aP.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["+41 00 000 00 00"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":293,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Actual aP string-only/truncating storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"billingaddress_email":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":303},"billingaddress_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":312,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"user_duplicate_config":{"description":"Accepts a decoded object/array or a JSON-encoded string; non-array/invalid JSON becomes defaults. Known keys are normalized: booleans; candidate threshold clamped0..100; nullable auto-merge threshold1..100; max_merge_users2..10; newest/oldest strategy with default; recognized unique field rules with weights1..10. Explicit rules:[] clears rules. Unknown keys/rules are discarded; hash/version are computed server-side. Changing matching rules invalidates cached duplicate candidates, indexes and worker state.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[{"enabled":true,"candidate_threshold":60,"rules":[]}],"x-edoobox-canonicalSchema":{"type":"object"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":321,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Userduplicatefunc.php","line":28,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Actual normalizeConfigForSave preserves unchanged rule hashes and normalizes current state."},{"file":"application/models/v2/Userduplicatefunc.php","line":40,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Full known config defaults, coercion, clamping, hash/version semantics."},{"file":"application/models/v2/Userduplicatefunc.php","line":648,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Rule filtering, uniqueness and weight normalization."},{"file":"application/models/v2/Userduplicatefunc.php","line":968,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Allowed public data-field selectors for duplicate matching."},{"file":"application/models/v2/Userduplicatefunc.php","line":488,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Changed matching rules invalidate derived duplicate data, not only the stored JSON."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"admin_avatar_mode":{"description":"String-casts the input; exact symbol selects symbol, every other value selects face. Unsupported values default rather than fail.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["symbol"],"x-edoobox-canonicalSchema":{"enum":["face","symbol"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":344,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_recipient_type":{"description":"Strict recognized strings organization, billing_address and admin are stored; legacy email and every other value normalize to organization. This is a defaulting normalizer, not a rejecting input enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["organization"],"x-edoobox-canonicalSchema":{"enum":["organization","billing_address","admin"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":363,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_recipient_email":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":390},"account_billing_recipient_admin_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_(?:-35|[1-9][0-9]*)$","examples":["admin_012345abcdef_123456"],"description":"Canonical public admin v2 ID issued in this account; server validates the account-bound checksum and rejects raw IDs."},{"enum":[null,false,0,"0",""],"x-edoobox-id-kind":"empty-relation"}],"examples":["admin_012345abcdef_123456",null,false],"description":"Choose an account-owned administrator for billing. The exact clear sentinels null, false, 0, string 0 and the empty string normalize to no administrator. Positive database primary keys are rejected; numeric acceptance is confined to the literal clear sentinel 0.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":421,"reason":"The corrected inbound getter accepts a canonical tenant-bound admin v2 reference and normalizes explicit clear sentinels; positive raw primary keys are rejected.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":437,"reason":"The outbound setter encodes a positive database admin key as a public v2 ID and emits false for zero.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/API_DATA.php","line":1853,"reason":"Join updates call the field getter before persisting; response serialization calls the field setter at line1068. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_auto_charge_enabled":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":440,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"account_billing_topup_enabled":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":459,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"account_billing_topup_threshold":{"description":"Casts the input to PHP float and rounds to two decimal places. No positive minimum, currency range or rejecting numeric wire type is enforced by this getter.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[25.5],"x-edoobox-canonicalSchema":{"type":"number"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":478,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_topup_amount":{"description":"Casts the input to PHP float and rounds to two decimal places. No positive minimum, currency range or rejecting numeric wire type is enforced by this getter.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[25.5],"x-edoobox-canonicalSchema":{"type":"number"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"internal_all":{"description":"Loose PHP comparison to true controls enable/disable. Enabling verifies the internal_function subscription feature; disabling bypasses that enablement check. Enable also regenerates the internal access code; disable clears it.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"type":"boolean"},"x-edoobox-preconditions":["An enabled subscription feature is an account-dependent runtime precondition, not an unconditional JSON requirement."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":516,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":23,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"notification_minimummaximum":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":526,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"notification_booking":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":535,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"vat_included":{"description":"PHP loose comparison to exclusive stores 1, other values store 0. Response returns exclusive for stored 1 and including otherwise.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["including"],"x-edoobox-canonicalSchema":{"enum":["exclusive","including"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":544,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"vat_reverse_charge_texts":{"description":"An associative language-to-text array is filtered to configured language keys; values pass through aP. Non-array input and unknown keys produce/are removed into an empty JSON list. Readback JSON-decodes storage; malformed legacy JSON yields null.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[{"de":"Example tax information"}],"x-edoobox-canonicalSchema":{"type":"object","additionalProperties":{"type":"string"}},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":553,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Each recognized text is normalized by aP."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_sort":{"description":"PHP loose comparison to date selects date; every other input stores manuell. Readback maps the alternative to manual. Input and output spellings differ.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["date"],"x-edoobox-canonicalSchema":{"enum":["date","manual"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":580,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_func_1":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":589,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"offer_func_1_status":{"description":"PHP loose comparison to numeric 3 selects storage text 3, otherwise 4. Readback emits numeric 3 or 4.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[3],"x-edoobox-canonicalSchema":{"enum":[3,4]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":598,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_archiving":{"description":"Integer-casts the input, preserves modes 1/2/3 and maps all other values to 0. The output setter returns false for disabled.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[0],"x-edoobox-canonicalSchema":{"enum":[0,1,2,3]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":607,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_archiving_reference_date":{"description":"Exact string start selects start; every other value selects visibility. This is normalization, not a rejecting enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["visibility"],"x-edoobox-canonicalSchema":{"enum":["start","visibility"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":634,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"news_and_tricks":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":644,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_create_admin":{"description":"Integer-casts the input. Modes 1 and 2 remain selected; all other inputs normalize to disabled 0. Readback uses false for disabled.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[1],"x-edoobox-canonicalSchema":{"enum":[0,1,2]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":653,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_userlogin":{"description":"PHP integer-casts the input; 2, 3 and 4 remain their numeric modes, other inputs become 1. The mode list describes normalized output, not a rejecting input enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[1],"x-edoobox-canonicalSchema":{"enum":[1,2,3,4]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":686,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_login":{"description":"Integer-casts input: 2 stores y, 3 stores p, all other values store n. Response maps n/y/p to 1/2/3 and unmatched stored values to null.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[1],"x-edoobox-canonicalSchema":{"enum":[1,2,3]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":713,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_login_global_users":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":738,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_cancellation":{"description":"PHP integer-casts the input; 2, 3 and 4 remain their numeric modes, other inputs become 1. The mode list describes normalized output, not a rejecting input enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[1],"x-edoobox-canonicalSchema":{"enum":[1,2,3,4]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":757,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cancellation_admin_confirmation_mail":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":784,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_reservation":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":803,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_paymentpage":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":812,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_sendinfo":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":822,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_label_notifications":{"description":"Non-array input becomes []. For each rule: trim a nonempty label of at most 15 characters, retain its first occurrence, require a PHP-valid email and a selected-account custom non-PDF email template. Invalid/duplicate entries are skipped, not a whole-field rejection. Storage uses internal template IDs only; response emits public emailtemplate v2 IDs.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[[]],"x-edoobox-canonicalSchema":{"type":"array","items":{"type":"object","required":["label","email","emailtemplate"],"properties":{"label":{"type":"string","minLength":1,"maxLength":15},"email":{"type":"string"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$"}},"additionalProperties":false}},"x-edoobox-preconditions":["The referenced template must already exist for this account; empty [] is a valid disabling input."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":831,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3060,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Actual rule filtering and per-account template lookup."},{"file":"application/models/v2/Settings.php","line":3105,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Response filter emits typed public emailtemplate IDs."},{"file":"application/models/v2/Settings.php","line":3159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Template must belong to the account and be an email template, not PDF."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_send_pdf":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":850,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_send_ics":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":859,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_reminder":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":878,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_reminder_hours":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":887,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"booking_visitconfirmation":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":902,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_number":{"description":"The hook integer-casts the supplied value, rejects results outside 1..999999999, and rejects an already-used booking number in the selected account. A successful update stores the counter; coercible text is accepted, so a rejecting integer wire type would be inaccurate.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[100001],"x-edoobox-canonicalSchema":{"type":"integer","minimum":1,"maximum":999999999},"x-edoobox-preconditions":["The chosen number must not already occur in the selected account."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":937,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":288,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual hook range check and tenant-scoped uniqueness query."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_percent":{"description":"Integer-casts then clamps to 0 through 100. Out-of-range/coercible input is normalized instead of rejected. The response setter integer-casts stored rows; malformed legacy rows are not clamped on read.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[50],"x-edoobox-canonicalSchema":{"type":"integer","minimum":0,"maximum":100},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":948,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_notvisitmail":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":975,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_cert_senddelayhours":{"description":"Integer-casts then clamps to 0 through 1440. Out-of-range/coercible input is normalized instead of rejected. The response setter integer-casts stored rows; malformed legacy rows are not clamped on read.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[50],"x-edoobox-canonicalSchema":{"type":"integer","minimum":0,"maximum":1440},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":996,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_validation_url":{"description":"Loose PHP comparison to true controls enable/disable. Enabling verifies the certificate_validation subscription feature; disabling bypasses that enablement check. Writes the certificate-validation flag.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"type":"boolean"},"x-edoobox-preconditions":["An enabled subscription feature is an account-dependent runtime precondition, not an unconditional JSON requirement."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1024,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":40,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_attachpdf":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1046,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_cert_attachpdfpercent":{"description":"Integer-casts then clamps to 0 through 100. Out-of-range/coercible input is normalized instead of rejected. The response setter integer-casts stored rows; malformed legacy rows are not clamped on read.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[50],"x-edoobox-canonicalSchema":{"type":"integer","minimum":0,"maximum":100},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1206,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_secondemail":{"description":"Getter maps PHP truthiness to integer enable/disable. Disabling always updates the flag. Enabling requires an existing stored destination field, or a supplied recognized booking_cert_secondemail_field public field key. A missing/unrecognized destination returns false from the hook. The companion field is conditionally required based on stored account state, not on every request.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"type":"boolean"},"x-edoobox-preconditions":["When no destination is stored, enabling requires booking_cert_secondemail_field in the same data object."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1094,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":641,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_secondemail_field":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. An unknown scalar key clears it; readback is false when empty, a public key when recognized, and null for unmatched legacy columns. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1115,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_secondemail":{"description":"Getter maps PHP truthiness to integer enable/disable. Disabling always updates the flag. Enabling requires an existing stored destination field, or a supplied recognized booking_secondemail_field public field key. A missing/unrecognized destination returns false from the hook. The companion field is conditionally required based on stored account state, not on every request.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"type":"boolean"},"x-edoobox-preconditions":["When no destination is stored, enabling requires booking_secondemail_field in the same data object."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1150,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":693,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_secondemail_field":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. An unknown scalar key clears it; readback is false when empty, a public key when recognized, and null for unmatched legacy columns. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1171,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_offerlist_secondemail_field":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. An unknown scalar key clears it; readback is false when empty, a public key when recognized, and null for unmatched legacy columns. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1233,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_offerlist_cancel_mail":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1268,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_allow_promotions":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1288,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_designation_promotions":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":50,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":1297},"skip_counter":false,"question_deliverables_show":false,"multi_offer_show":false,"multi_offer_child_limit":false,"automatic_deletion":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1367,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"edsupport_access":{"description":"true or string true becomes 1; false, string false, null and empty string become 0; otherwise positive integer-cast values are retained, nonpositive values become 0. The getter does not reject noncanonical types.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"boolean"},{"type":"integer","minimum":0}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1395,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"payment_confirmation_mail":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1428,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"barcode_type":{"x-edoobox-knownInputs":["pdf417","qrcode","datamatrix"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["pdf417"],"x-edoobox-sourceLine":1437},"barcode_mobile_wallet":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1461,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mobile_wallet_background_color":{"description":"Trims/string-casts; six hexadecimal digits with optional # normalize to uppercase #RRGGBB. Empty or malformed values become '#0068B0'. No rejecting input pattern is applied.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["#12ab34"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1470,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_foreground_color":{"description":"Trims/string-casts; six hexadecimal digits with optional # normalize to uppercase #RRGGBB. Empty or malformed values become ''. No rejecting input pattern is applied.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["#12ab34"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1479,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_use_offer_image":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1488,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mobile_wallet_fallback_image_url":{"description":"String-casts, strips HTML tags, trims and byte-truncates to 500 bytes. Despite the field name, no URL syntax or scheme validator is applied.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["https://example.invalid/support"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_display_name":{"description":"String-casts, strips HTML tags, trims and byte-truncates to 150 bytes. The response setter only string-casts stored data.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["Example organization"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1506,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_support_url":{"description":"String-casts, strips HTML tags, trims and byte-truncates to 500 bytes. Despite the field name, no URL syntax or scheme validator is applied.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["https://example.invalid/support"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1515,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"barcode_ticket":{"x-edoobox-knownInputs":[false,"always","offer"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":[false],"x-edoobox-sourceLine":1524},"collective_booking_max":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1548,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"collective_booking_identify":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. Unknown keys return null; readback is false when empty, a public key when recognized, and null for unmatched legacy columns. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1563,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"collective_booking_identify_privacy":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. The clear sentinel is stored as NO; unknown keys store empty text. Readback is false for NO, a public key when recognized, and null otherwise. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1586,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"collective_booking_identify_obligatory":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1610,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"collective_booking_master":{"x-edoobox-knownInputs":["manual","all",false],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["manual"],"x-edoobox-sourceLine":1673},"security_headers_csp":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1654,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design_subcategory":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1699,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"legaltext_atc":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1708,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"legaltext_ri":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1717,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"legaltext_pp":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1726,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"attendance_config":{"type":"array","items":{"type":"object","properties":{"status":{"type":["number","string"],"description":"Converted to integer by the callback."},"order":{"type":["number","string"]},"counter":{},"val":{},"color":{},"help":{}},"additionalProperties":true,"required":["status"]},"description":"The converted statuses 0, 1 and 2 must each occur exactly once among processed items. Counter values outside 0..100 become 0. Extra statuses are supported; processing stops after 41 entries or on a missing status.","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":153,"reason":"PUT sorts the status-record array, casts status/order and normalizes counter.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settingsfunc.php","line":208,"reason":"Exactly three required-status occurrences are checked.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":1739,"reason":"Response returns ordered status records as a JSON array.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"language":{"type":"object","properties":{"active":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"description":"Active language codes in desired order. Unknown codes are ignored. Account language configuration governs availability.","required":["active"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":77,"reason":"PUT expects an active language-code array inside an object.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":1759,"reason":"The resource Structure response placeholder is false; enriched action responses may differ.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"edsubscription":false,"edsubscription_testinguntildate":false,"versions":false,"pdf_engine":{"description":"PHP switch maps input mode 1 to dompdf; mode 2 and other inputs default to mpdf. Readback maps stored dompdf/mpdf to numeric 1/2, with null for unknown stored text.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[2],"x-edoobox-canonicalSchema":{"enum":[1,2]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1821,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"pdf_edit_restriction_password":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1851,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"pdf_paper_size":{"description":"Requires an exact installed library paper-size key after case folding, before aP trim/storage normalization. Unknown values return strict false from the getter and the final generic joined writer rejects ED80100 before joined persistence. The existing case-insensitive pattern reflects those installed meaningful paper-size strings; the own parent and relation predicates remain independently checked.","x-edoobox-schemaStatus":"source-reviewed-validation","examples":["a4"],"x-edoobox-canonicalSchema":{"enum":["4a0","2a0","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a10","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","b10","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","ra0","ra1","ra2","ra3","ra4","sra0","sra1","sra2","sra3","sra4","letter","half-letter","legal","ledger","tabloid","executive","folio","commercial #10 envelope","catalog #10 1/2 envelope","8.5x11","8.5x14","11x17"]},"type":"string","pattern":"^(?:4[aA]0|2[aA]0|[aA]0|[aA]1|[aA]2|[aA]3|[aA]4|[aA]5|[aA]6|[aA]7|[aA]8|[aA]9|[aA]10|[bB]0|[bB]1|[bB]2|[bB]3|[bB]4|[bB]5|[bB]6|[bB]7|[bB]8|[bB]9|[bB]10|[cC]0|[cC]1|[cC]2|[cC]3|[cC]4|[cC]5|[cC]6|[cC]7|[cC]8|[cC]9|[cC]10|[rR][aA]0|[rR][aA]1|[rR][aA]2|[rR][aA]3|[rR][aA]4|[sS][rR][aA]0|[sS][rR][aA]1|[sS][rR][aA]2|[sS][rR][aA]3|[sS][rR][aA]4|[lL][eE][tT][tT][eE][rR]|[hH][aA][lL][fF]\\-[lL][eE][tT][tT][eE][rR]|[lL][eE][gG][aA][lL]|[lL][eE][dD][gG][eE][rR]|[tT][aA][bB][lL][oO][iI][dD]|[eE][xX][eE][cC][uU][tT][iI][vV][eE]|[fF][oO][lL][iI][oO]|[cC][oO][mM][mM][eE][rR][cC][iI][aA][lL]\\ \\#10\\ [eE][nN][vV][eE][lL][oO][pP][eE]|[cC][aA][tT][aA][lL][oO][gG]\\ \\#10\\ 1/2\\ [eE][nN][vV][eE][lL][oO][pP][eE]|8\\.5[xX]11|8\\.5[xX]14|11[xX]17)$","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1870,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"vendor/dompdf/dompdf/src/Adapter/CPDF.php","line":42,"sha256":"7dca4a096b08c006f12bfccd733068223d92e502b9a625c49bd919373f714001","reason":"Installed Dompdf paper-size key list, matched case-insensitively by the source getter."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Normal fields check strict false; joined fields instead collect getter output at 1793 and execute it at 1827 without that rejection. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"pdf_custom_css":{"description":"Parses CSS, removes non-declaration blocks, keeps selectors starting .custom_ or *, and filters rule value node classes. font-family is retained only when a family is among Pdf::$fonts. Stores compact CSS base64; readback decodes/formats it or returns empty text. Parser errors and PDF renderer prerequisites are not a closed JSON Schema grammar.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[".custom_example { color: #123456; }"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-preconditions":["CSS parser grammar and configured font support are checked by source/parser, not replicated as a misleading JSON pattern."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1892,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/libraries/edoobox/integrations/pdf/Pdf.php","line":16,"sha256":"b286cdc4d20e02601db9acc35b06564357f10378961e72cb8f4e22e64b3a3254","reason":"Allowed PDF font keys used by the CSS normalizer."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"dinaco_client_id":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":1960},"dinaco_client_secret":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":1979},"gtm_mess_id":{"description":"For input longer than five bytes, a non-anchored G-[a-zA-z0-9]{5,20} substring must occur or the value becomes empty. Short values are retained. aP then trims, replaces newlines and truncates. This is not a strict GA4-ID pattern validator.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["G-EXAMPLE1234"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1998,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP normalization after the substring test."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"gtm_container_id":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":2024},"meta_html":{"description":"Parses supplied HTML and keeps only meta tags whose name is google-site-verification or facebook-domain-verification. Only their name/content attributes are serialized, then aP truncates to 250 stored characters. This is a lossy sanitizer, not a rejecting HTML schema.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[""],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2044,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"system_attachments_file_naming":{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{},"additionalProperties":{"type":"string"}},"description":"Map keyed by an emailtemplate public ID or ics. Each value maps language codes to file-name text. Filename characters and suffix are normalized; unknown templates/languages are ignored.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2076,"reason":"Input iterates template/ics keys and language-to-name objects; edid occurs on map keys.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":2106,"reason":"Response maps stored keys back to public emailtemplate IDs or ics.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"website_integration":{"enum":["iframe","wordpress"],"x-edoobox-sourceLine":2132},"website_url":{"description":"Accepts empty string or a string accepted by PHP FILTER_VALIDATE_URL. Other values return strict false and API_DATA rejects the update. No restriction to HTTP/HTTPS is imposed by this getter.","x-edoobox-schemaStatus":"source-reviewed-validation","examples":["https://example.invalid"],"x-edoobox-canonicalSchema":{"type":"string"},"type":"string","x-edoobox-preconditions":["PHP FILTER_VALIDATE_URL is the rejecting syntax check; JSON Schema format:uri is not asserted as an exact equivalent."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Strict false getter rejects invalid URL inputs. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"admin_custom_permissions":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":2172,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"admin_academic_title_use":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":2192,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["id","congress_module","root_category","shortcut","organization_country","organization_currency","skip_counter","question_deliverables_show","multi_offer_show","multi_offer_child_limit","edsubscription","edsubscription_testinguntildate","versions"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Account-Einstellungen ändern","value":{"data":{"organization_name":"Beispielorganisation","firstweekday":1,"automatic_deletion":180},"id":"account_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/{id} Account-Einstellungen abrufen Liest die zentralen Einstellungen des ausgewählten Accounts sowie Sprachen, Archiv-/Papierkorb-IDs, Währungen, Abonnement- und Versionsinformationen. Die Antwort reduziert die generische Auswahl auf das erste gefundene Einstellungsobjekt und ergänzt Account-Metadaten. Bei Bedarf werden Versions-Caches für Admin-Konfigurationsvorlagen und Mehrwertsteuer befüllt. Keine Zugangsdaten aus der Antwort veröffentlichen. - Operation-ID: get_v2_settings_id - Dokumentation: /docs/api/v2/operations/get-v2-settings-id-46ae32622163/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Account-ID aus den Einstellungen. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Account-ID aus den Einstellungen.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_id","summary":"Account-Einstellungen abrufen","description":"Liest die zentralen Einstellungen des ausgewählten Accounts sowie Sprachen, Archiv-/Papierkorb-IDs, Währungen, Abonnement- und Versionsinformationen. Die Antwort reduziert die generische Auswahl auf das erste gefundene Einstellungsobjekt und ergänzt Account-Metadaten. Bei Bedarf werden Versions-Caches für Admin-Konfigurationsvorlagen und Mehrwertsteuer befüllt. Keine Zugangsdaten aus der Antwort veröffentlichen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Account-ID aus den Einstellungen.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Ein einzelnes Einstellungsobjekt mit zusätzlichen Account-Metadaten, auch bei /list.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","organization_name":"Beispielorganisation","organization_language":"de","root_archive":"category_012345abcdef_123456","root_trash":"category_012345abcdef_123456","language":{"languages":["Deutsch","English"],"active":["de"],"inactive":["en"]},"user_currencys":["CHF"],"account_billing_recipient_admin_id":false},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"firstweekday":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"integer"},"format_date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":53,"type":"integer"},"format_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"type":"integer"},"format_calendar_grid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":71,"type":"integer"},"format_timezone":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":5,"reason":"PUT consumes a zone-name string instead of the integer get placeholder.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":85,"reason":"Response returns the account timezone zone_name.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"congress_module":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"boolean"},"format_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"root_category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":130,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["rootcategory_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"type":"string"},"organization_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":150,"type":"string"},"organization_contactperson":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"organization_street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168,"type":"string"},"organization_postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":177,"type":"string"},"organization_town":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":186,"type":"string"},"organization_country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":195,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["organizationcountry_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"organization_language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":205,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/langedoobox_helper.php","line":731,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangIDByName returns configured entry, with fallback for unknown names."},{"file":"application/helpers/langedoobox_helper.php","line":566,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangById converts internal entries back to public language keys."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":214,"type":"string"},"organization_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":223,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP text storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_email":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":57,"reason":"PUT callback validates the trimmed email syntax, checks raw and encoded lengths, and requires an exact aP/aQ identity before returning storage. False is rejected by generic update as ED90100 before a write.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","lineEnd":75},{"file":"application/models/v2/Settings.php","line":237,"reason":"Incoming model getter is bounded to200 characters; generic PUT uses the dedicated validation hook; response unescapes stored text.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","lineEnd":246},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":247,"type":"string"},"billingaddress_organization":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":257,"type":"string"},"billingaddress_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":266,"type":"string"},"billingaddress_street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"billingaddress_postcode_town":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":284,"type":"string"},"billingaddress_phone":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":293,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Actual aP string-only/truncating storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"billingaddress_email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":303,"type":"string"},"billingaddress_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":312,"type":"boolean"},"user_duplicate_config":{"type":"object","required":["version","enabled","candidate_threshold","auto_merge_threshold","survivor_strategy","max_merge_users","rules","rules_hash","matching_rules_hash"],"properties":{"version":{"const":2},"enabled":{"type":"boolean"},"candidate_threshold":{"type":"integer","minimum":0,"maximum":100},"auto_merge_threshold":{"type":["integer","null"],"minimum":1,"maximum":100},"survivor_strategy":{"enum":["newest","oldest"]},"max_merge_users":{"type":"integer","minimum":2,"maximum":10},"rules":{"type":"array","items":{"type":"object","required":["field","enabled","required","weight"],"properties":{"field":{"enum":["gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"enabled":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer","minimum":1,"maximum":10}},"additionalProperties":false}},"rules_hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"matching_rules_hash":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":321,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Userduplicatefunc.php","line":28,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Actual normalizeConfigForSave preserves unchanged rule hashes and normalizes current state."},{"file":"application/models/v2/Userduplicatefunc.php","line":40,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Full known config defaults, coercion, clamping, hash/version semantics."},{"file":"application/models/v2/Userduplicatefunc.php","line":648,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Rule filtering, uniqueness and weight normalization."},{"file":"application/models/v2/Userduplicatefunc.php","line":968,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Allowed public data-field selectors for duplicate matching."},{"file":"application/models/v2/Userduplicatefunc.php","line":488,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Changed matching rules invalidate derived duplicate data, not only the stored JSON."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"admin_avatar_mode":{"enum":["face","symbol"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":344,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_recipient_type":{"enum":["organization","billing_address","admin"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":363,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_recipient_email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":390,"type":"string"},"account_billing_recipient_admin_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_(?:-35|[1-9][0-9]*)$","examples":["admin_012345abcdef_123456"],"description":"Canonical public admin v2 ID issued in this account; server validates the account-bound checksum and rejects raw IDs."},{"const":false,"x-edoobox-id-kind":"empty-relation"}],"description":"Billing administrator as a public admin v2 ID; false when unset. Internal database primary keys are never serialized.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":421,"reason":"The corrected inbound getter accepts a canonical tenant-bound admin v2 reference and normalizes explicit clear sentinels; positive raw primary keys are rejected.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":437,"reason":"The outbound setter encodes a positive database admin key as a public v2 ID and emits false for zero.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/API_DATA.php","line":1853,"reason":"Join updates call the field getter before persisting; response serialization calls the field setter at line1068. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"account_billing_auto_charge_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":440,"type":"boolean"},"account_billing_topup_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":459,"type":"boolean"},"account_billing_topup_threshold":{"type":"number","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":478,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_topup_amount":{"type":"number","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"internal_all":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":516,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":23,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"notification_minimummaximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":526,"type":"boolean"},"notification_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":535,"type":"boolean"},"vat_included":{"enum":["exclusive","including"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":544,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"vat_reverse_charge_texts":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":553,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Each recognized text is normalized by aP."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_sort":{"enum":["date","manual"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":580,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_func_1":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":589,"type":"boolean"},"offer_func_1_status":{"enum":[3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":598,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_archiving":{"enum":[1,2,3,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":607,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_archiving_reference_date":{"enum":["start","visibility"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":634,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"news_and_tricks":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":644,"type":"boolean"},"booking_create_admin":{"enum":[1,2,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":653,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_userlogin":{"enum":[1,2,3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":686,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_login":{"enum":[1,2,3,null],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":713,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_login_global_users":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":738,"type":"boolean"},"booking_cancellation":{"enum":[1,2,3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":757,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cancellation_admin_confirmation_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":784,"type":"boolean"},"booking_reservation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":803,"type":"boolean"},"booking_paymentpage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":812,"type":"boolean"},"booking_sendinfo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":822,"type":"boolean"},"booking_label_notifications":{"type":"array","items":{"type":"object","required":["label","email","emailtemplate"],"properties":{"label":{"type":"string","minLength":1,"maxLength":15},"email":{"type":"string"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$"}},"additionalProperties":false},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":831,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3060,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Actual rule filtering and per-account template lookup."},{"file":"application/models/v2/Settings.php","line":3105,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Response filter emits typed public emailtemplate IDs."},{"file":"application/models/v2/Settings.php","line":3159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Template must belong to the account and be an email template, not PDF."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_send_pdf":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":850,"type":"boolean"},"booking_send_ics":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":859,"type":"boolean"},"booking_reminder":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":878,"type":"boolean"},"booking_reminder_hours":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":887,"type":"integer"},"booking_visitconfirmation":{"enum":[1,2,false]},"booking_number":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":937,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":288,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual hook range check and tenant-scoped uniqueness query."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_percent":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":948,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_notvisitmail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":975,"type":"boolean"},"booking_cert_senddelayhours":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":996,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_validation_url":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1024,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":40,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_attachpdf":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1046,"type":"boolean"},"booking_cert_attachpdfpercent":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1206,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_secondemail":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1094,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":641,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1115,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_secondemail":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1150,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":693,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1171,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_offerlist_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1233,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_offerlist_cancel_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1268,"type":"boolean"},"booking_allow_promotions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1288,"type":"boolean"},"booking_designation_promotions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1297,"type":"string"},"skip_counter":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1306,"type":"integer"},"question_deliverables_show":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1316,"type":"boolean"},"multi_offer_show":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1336,"type":"boolean"},"multi_offer_child_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1346,"type":"integer"},"automatic_deletion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1367},"edsupport_access":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1395,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"payment_confirmation_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1428,"type":"boolean"},"barcode_type":{"enum":["pdf417","qrcode","datamatrix"]},"barcode_mobile_wallet":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1461,"type":"boolean"},"mobile_wallet_background_color":{"type":"string","pattern":"^#[0-9A-F]{6}$","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1470,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_foreground_color":{"type":"string","pattern":"^(?:#[0-9A-F]{6})?$","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1479,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_use_offer_image":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1488,"type":"boolean"},"mobile_wallet_fallback_image_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_display_name":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1506,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_support_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1515,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"barcode_ticket":{"enum":[false,"always","offer"]},"collective_booking_max":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1548,"type":"integer"},"collective_booking_identify":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1563,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"collective_booking_identify_privacy":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1586,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"collective_booking_identify_obligatory":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1610,"type":"boolean"},"collective_booking_master":{"enum":["manual","all",false]},"security_headers_csp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1654,"type":"boolean"},"design_subcategory":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1699,"type":"boolean"},"legaltext_atc":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1708,"type":"boolean"},"legaltext_ri":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1717,"type":"boolean"},"legaltext_pp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1726,"type":"boolean"},"attendance_config":{"type":"array","items":{"type":"object","properties":{"status":{"type":["integer","string"],"description":"The key is derived with an integer cast; stored status representation is preserved in the record."},"order":{"type":"integer"},"counter":{},"val":{},"color":{},"help":{},"lock":{"type":"boolean"}},"additionalProperties":true,"required":["status","order"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":153,"reason":"PUT sorts the status-record array, casts status/order and normalizes counter.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settingsfunc.php","line":208,"reason":"Exactly three required-status occurrences are checked.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":1739,"reason":"Response returns ordered status records as a JSON array.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"language":{"type":"object","properties":{"languages":{"type":"array","items":{"type":"string"}},"active":{"type":["object","array"]},"inactive":{"type":["object","array"]}},"additionalProperties":true},"edsubscription":{"type":"string","pattern":"^edsubscription_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"edsubscription_testinguntildate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1786},"versions":{"type":"object","properties":{},"additionalProperties":true},"pdf_engine":{"enum":[1,2,null],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1821,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"pdf_edit_restriction_password":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1851,"type":"boolean"},"pdf_paper_size":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1870,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"vendor/dompdf/dompdf/src/Adapter/CPDF.php","line":42,"sha256":"7dca4a096b08c006f12bfccd733068223d92e502b9a625c49bd919373f714001","reason":"Installed Dompdf paper-size key list, matched case-insensitively by the source getter."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Normal fields check strict false; joined fields instead collect getter output at 1793 and execute it at 1827 without that rejection. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"pdf_custom_css":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1892,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/libraries/edoobox/integrations/pdf/Pdf.php","line":16,"sha256":"b286cdc4d20e02601db9acc35b06564357f10378961e72cb8f4e22e64b3a3254","reason":"Allowed PDF font keys used by the CSS normalizer."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"dinaco_client_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1960,"type":"string"},"dinaco_client_secret":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1979,"x-edoobox-sensitive":true},"gtm_mess_id":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1998,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP normalization after the substring test."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"gtm_container_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2024,"type":"string"},"meta_html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2044,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"system_attachments_file_naming":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{},"additionalProperties":{"type":"string"}}},{"type":"array","maxItems":0}],"description":"Associative map, or [] when there are no naming rules.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2076,"reason":"Input iterates template/ics keys and language-to-name objects; edid occurs on map keys.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":2106,"reason":"Response maps stored keys back to public emailtemplate IDs or ics.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"website_integration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2132},"website_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Strict false getter rejects invalid URL inputs. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"admin_custom_permissions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2172,"type":"boolean"},"admin_academic_title_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2192,"type":"boolean"},"root_archive":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"root_trash":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"user_currencys":{"type":"array","items":{"type":"string"}},"stripe_public_key":{"type":["string","boolean"]}},"additionalProperties":true}],"description":"Einzelnes Account-Einstellungsobjekt; auch die Listenroute reduziert auf das erste gefundene Objekt."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-sensitiveResponseFields":["dinaco_client_secret"],"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id} Account-Einstellungen ändern Aktualisiert die übergebenen zentralen Account-Einstellungen. Es werden nur bekannte, schreibbare Structure-Felder verarbeitet; Datums-/Zahlen-/Schalterwerte werden gemäß dem jeweiligen Feld normalisiert. Dynamische Datenfeldnamen für Mehrfachanmeldung oder Zweitemail müssen zur aktuellen Teilnehmerdatenstruktur gehören. Sprach-, Präsenz-, Abrechnungs- und Buchungsregeln können weitere native Validierungen und Folgeänderungen auslösen; Abonnement-/Berechtigungsfreigaben bleiben maßgeblich. - Operation-ID: put_v2_settings_id - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-2ec19893781e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche account-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche account-ID.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "organization_name": "Beispielorganisation", "firstweekday": 1, "automatic_deletion": 180 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "organization_name": "Beispielorganisation", "firstweekday": 1, "automatic_deletion": 180 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"organization_name\":\"Beispielorganisation\",\"firstweekday\":1,\"automatic_deletion\":180}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id","summary":"Account-Einstellungen ändern","description":"Aktualisiert die übergebenen zentralen Account-Einstellungen. Es werden nur bekannte, schreibbare Structure-Felder verarbeitet; Datums-/Zahlen-/Schalterwerte werden gemäß dem jeweiligen Feld normalisiert. Dynamische Datenfeldnamen für Mehrfachanmeldung oder Zweitemail müssen zur aktuellen Teilnehmerdatenstruktur gehören. Sprach-, Präsenz-, Abrechnungs- und Buchungsregeln können weitere native Validierungen und Folgeänderungen auslösen; Abonnement-/Berechtigungsfreigaben bleiben maßgeblich.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche account-ID.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Account-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"firstweekday":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":44,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"format_date":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":53,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"format_time":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":62,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"format_calendar_grid":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":71,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"format_timezone":{"type":"string","description":"Existing IANA timezone name, for example Europe/Zurich; the update callback looks up zone_name.","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":5,"reason":"PUT consumes a zone-name string instead of the integer get placeholder.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":85,"reason":"Response returns the account timezone zone_name.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"congress_module":false,"format_number":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":112,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":121},"root_category":false,"shortcut":false,"organization_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":150},"organization_contactperson":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":159},"organization_street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":168},"organization_postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":177},"organization_town":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":200,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":186},"organization_country":false,"organization_language":{"description":"Maps a configured language key to its internal language entry; unknown keys default internally. Response returns the configured language key or null for an unmatched legacy value. This is a language selector, not an externally exposed numeric ID.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["de"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":205,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/langedoobox_helper.php","line":731,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangIDByName returns configured entry, with fallback for unknown names."},{"file":"application/helpers/langedoobox_helper.php","line":566,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangById converts internal entries back to public language keys."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"organization_phone":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":214},"organization_url":{"description":"Removes exact lowercase leading http:// or https:// before aP trim/newline/truncation to 250 stored characters. No URL syntax is required; other schemes and uppercase prefixes are not normalized in that step.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["https://example.invalid"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":223,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP text storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"organization_email":{"type":"string","maxLength":200,"description":"Trimmed input must pass PHP FILTER_VALIDATE_EMAIL. Native PUT rejects raw input over200 UTF-8 characters (including outer whitespace), escaped trimmed storage over200 characters, or any aP/aQ roundtrip change to the validated address. Rejection returns the existing invalid-email path before persistence; valid addresses are preserved.","x-edoobox-validation":"PHP FILTER_VALIDATE_EMAIL after trim; raw UTF-8 length<=200; escaped trimmed length<=200; aQ(aP(trimmed,true,200))===trimmed","x-edoobox-maxStoredLength":200,"x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":57,"reason":"PUT callback validates the trimmed email syntax, checks raw and encoded lengths, and requires an exact aP/aQ identity before returning storage. False is rejected by generic update as ED90100 before a write.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","lineEnd":75},{"file":"application/models/v2/Settings.php","line":237,"reason":"Incoming model getter is bounded to200 characters; generic PUT uses the dedicated validation hook; response unescapes stored text.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","lineEnd":246},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"organization_currency":false,"billingaddress_organization":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":257},"billingaddress_user":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":50,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":266},"billingaddress_street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":275},"billingaddress_postcode_town":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":100,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":284},"billingaddress_phone":{"description":"Trims the value then applies aP text normalization (trim/newline replacement/truncation to 250 stored characters). It does not validate telephone syntax. Some nonscalar values cause a PHP type error before aP.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["+41 00 000 00 00"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":293,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Actual aP string-only/truncating storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"billingaddress_email":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":303},"billingaddress_status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":312,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"user_duplicate_config":{"description":"Accepts a decoded object/array or a JSON-encoded string; non-array/invalid JSON becomes defaults. Known keys are normalized: booleans; candidate threshold clamped0..100; nullable auto-merge threshold1..100; max_merge_users2..10; newest/oldest strategy with default; recognized unique field rules with weights1..10. Explicit rules:[] clears rules. Unknown keys/rules are discarded; hash/version are computed server-side. Changing matching rules invalidates cached duplicate candidates, indexes and worker state.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[{"enabled":true,"candidate_threshold":60,"rules":[]}],"x-edoobox-canonicalSchema":{"type":"object"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":321,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Userduplicatefunc.php","line":28,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Actual normalizeConfigForSave preserves unchanged rule hashes and normalizes current state."},{"file":"application/models/v2/Userduplicatefunc.php","line":40,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Full known config defaults, coercion, clamping, hash/version semantics."},{"file":"application/models/v2/Userduplicatefunc.php","line":648,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Rule filtering, uniqueness and weight normalization."},{"file":"application/models/v2/Userduplicatefunc.php","line":968,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Allowed public data-field selectors for duplicate matching."},{"file":"application/models/v2/Userduplicatefunc.php","line":488,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Changed matching rules invalidate derived duplicate data, not only the stored JSON."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"admin_avatar_mode":{"description":"String-casts the input; exact symbol selects symbol, every other value selects face. Unsupported values default rather than fail.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["symbol"],"x-edoobox-canonicalSchema":{"enum":["face","symbol"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":344,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"account_billing_recipient_type":{"description":"Strict recognized strings organization, billing_address and admin are stored; legacy email and every other value normalize to organization. This is a defaulting normalizer, not a rejecting input enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["organization"],"x-edoobox-canonicalSchema":{"enum":["organization","billing_address","admin"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":363,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"account_billing_recipient_email":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":390},"account_billing_recipient_admin_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_(?:-35|[1-9][0-9]*)$","examples":["admin_012345abcdef_123456"],"description":"Canonical public admin v2 ID issued in this account; server validates the account-bound checksum and rejects raw IDs."},{"enum":[null,false,0,"0",""],"x-edoobox-id-kind":"empty-relation"}],"examples":["admin_012345abcdef_123456",null,false],"description":"Choose an account-owned administrator for billing. The exact clear sentinels null, false, 0, string 0 and the empty string normalize to no administrator. Positive database primary keys are rejected; numeric acceptance is confined to the literal clear sentinel 0.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":421,"reason":"The corrected inbound getter accepts a canonical tenant-bound admin v2 reference and normalizes explicit clear sentinels; positive raw primary keys are rejected.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":437,"reason":"The outbound setter encodes a positive database admin key as a public v2 ID and emits false for zero.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/API_DATA.php","line":1853,"reason":"Join updates call the field getter before persisting; response serialization calls the field setter at line1068. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"account_billing_auto_charge_enabled":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":440,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"account_billing_topup_enabled":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":459,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"account_billing_topup_threshold":{"description":"Casts the input to PHP float and rounds to two decimal places. No positive minimum, currency range or rejecting numeric wire type is enforced by this getter.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[25.5],"x-edoobox-canonicalSchema":{"type":"number"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":478,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"account_billing_topup_amount":{"description":"Casts the input to PHP float and rounds to two decimal places. No positive minimum, currency range or rejecting numeric wire type is enforced by this getter.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[25.5],"x-edoobox-canonicalSchema":{"type":"number"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"internal_all":{"description":"Loose PHP comparison to true controls enable/disable. Enabling verifies the internal_function subscription feature; disabling bypasses that enablement check. Enable also regenerates the internal access code; disable clears it.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"type":"boolean"},"x-edoobox-preconditions":["An enabled subscription feature is an account-dependent runtime precondition, not an unconditional JSON requirement."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":516,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":23,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"notification_minimummaximum":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":526,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"notification_booking":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":535,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"vat_included":{"description":"PHP loose comparison to exclusive stores 1, other values store 0. Response returns exclusive for stored 1 and including otherwise.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["including"],"x-edoobox-canonicalSchema":{"enum":["exclusive","including"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":544,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"vat_reverse_charge_texts":{"description":"An associative language-to-text array is filtered to configured language keys; values pass through aP. Non-array input and unknown keys produce/are removed into an empty JSON list. Readback JSON-decodes storage; malformed legacy JSON yields null.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[{"de":"Example tax information"}],"x-edoobox-canonicalSchema":{"type":"object","additionalProperties":{"type":"string"}},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":553,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Each recognized text is normalized by aP."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"offer_sort":{"description":"PHP loose comparison to date selects date; every other input stores manuell. Readback maps the alternative to manual. Input and output spellings differ.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["date"],"x-edoobox-canonicalSchema":{"enum":["date","manual"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":580,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"offer_func_1":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":589,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"offer_func_1_status":{"description":"PHP loose comparison to numeric 3 selects storage text 3, otherwise 4. Readback emits numeric 3 or 4.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[3],"x-edoobox-canonicalSchema":{"enum":[3,4]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":598,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"offer_archiving":{"description":"Integer-casts the input, preserves modes 1/2/3 and maps all other values to 0. The output setter returns false for disabled.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[0],"x-edoobox-canonicalSchema":{"enum":[0,1,2,3]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":607,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"offer_archiving_reference_date":{"description":"Exact string start selects start; every other value selects visibility. This is normalization, not a rejecting enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["visibility"],"x-edoobox-canonicalSchema":{"enum":["start","visibility"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":634,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"news_and_tricks":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":644,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_create_admin":{"description":"Integer-casts the input. Modes 1 and 2 remain selected; all other inputs normalize to disabled 0. Readback uses false for disabled.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[1],"x-edoobox-canonicalSchema":{"enum":[0,1,2]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":653,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_userlogin":{"description":"PHP integer-casts the input; 2, 3 and 4 remain their numeric modes, other inputs become 1. The mode list describes normalized output, not a rejecting input enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[1],"x-edoobox-canonicalSchema":{"enum":[1,2,3,4]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":686,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_login":{"description":"Integer-casts input: 2 stores y, 3 stores p, all other values store n. Response maps n/y/p to 1/2/3 and unmatched stored values to null.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[1],"x-edoobox-canonicalSchema":{"enum":[1,2,3]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":713,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_login_global_users":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":738,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_cancellation":{"description":"PHP integer-casts the input; 2, 3 and 4 remain their numeric modes, other inputs become 1. The mode list describes normalized output, not a rejecting input enum.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[1],"x-edoobox-canonicalSchema":{"enum":[1,2,3,4]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":757,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_cancellation_admin_confirmation_mail":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":784,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_reservation":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":803,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_paymentpage":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":812,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_sendinfo":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":822,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_label_notifications":{"description":"Non-array input becomes []. For each rule: trim a nonempty label of at most 15 characters, retain its first occurrence, require a PHP-valid email and a selected-account custom non-PDF email template. Invalid/duplicate entries are skipped, not a whole-field rejection. Storage uses internal template IDs only; response emits public emailtemplate v2 IDs.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[[]],"x-edoobox-canonicalSchema":{"type":"array","items":{"type":"object","required":["label","email","emailtemplate"],"properties":{"label":{"type":"string","minLength":1,"maxLength":15},"email":{"type":"string"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$"}},"additionalProperties":false}},"x-edoobox-preconditions":["The referenced template must already exist for this account; empty [] is a valid disabling input."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":831,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3060,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Actual rule filtering and per-account template lookup."},{"file":"application/models/v2/Settings.php","line":3105,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Response filter emits typed public emailtemplate IDs."},{"file":"application/models/v2/Settings.php","line":3159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Template must belong to the account and be an email template, not PDF."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_send_pdf":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":850,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_send_ics":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":859,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_reminder":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":878,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_reminder_hours":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":887,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"booking_visitconfirmation":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":902,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_number":{"description":"The hook integer-casts the supplied value, rejects results outside 1..999999999, and rejects an already-used booking number in the selected account. A successful update stores the counter; coercible text is accepted, so a rejecting integer wire type would be inaccurate.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[100001],"x-edoobox-canonicalSchema":{"type":"integer","minimum":1,"maximum":999999999},"x-edoobox-preconditions":["The chosen number must not already occur in the selected account."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":937,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":288,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual hook range check and tenant-scoped uniqueness query."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_cert_percent":{"description":"Integer-casts then clamps to 0 through 100. Out-of-range/coercible input is normalized instead of rejected. The response setter integer-casts stored rows; malformed legacy rows are not clamped on read.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[50],"x-edoobox-canonicalSchema":{"type":"integer","minimum":0,"maximum":100},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":948,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_cert_notvisitmail":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":975,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_cert_senddelayhours":{"description":"Integer-casts then clamps to 0 through 1440. Out-of-range/coercible input is normalized instead of rejected. The response setter integer-casts stored rows; malformed legacy rows are not clamped on read.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[50],"x-edoobox-canonicalSchema":{"type":"integer","minimum":0,"maximum":1440},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":996,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_cert_validation_url":{"description":"Loose PHP comparison to true controls enable/disable. Enabling verifies the certificate_validation subscription feature; disabling bypasses that enablement check. Writes the certificate-validation flag.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"type":"boolean"},"x-edoobox-preconditions":["An enabled subscription feature is an account-dependent runtime precondition, not an unconditional JSON requirement."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1024,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":40,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_cert_attachpdf":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1046,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_cert_attachpdfpercent":{"description":"Integer-casts then clamps to 0 through 100. Out-of-range/coercible input is normalized instead of rejected. The response setter integer-casts stored rows; malformed legacy rows are not clamped on read.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[50],"x-edoobox-canonicalSchema":{"type":"integer","minimum":0,"maximum":100},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1206,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_cert_secondemail":{"description":"Getter maps PHP truthiness to integer enable/disable. Disabling always updates the flag. Enabling requires an existing stored destination field, or a supplied recognized booking_cert_secondemail_field public field key. A missing/unrecognized destination returns false from the hook. The companion field is conditionally required based on stored account state, not on every request.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"type":"boolean"},"x-edoobox-preconditions":["When no destination is stored, enabling requires booking_cert_secondemail_field in the same data object."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1094,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":641,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_cert_secondemail_field":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. An unknown scalar key clears it; readback is false when empty, a public key when recognized, and null for unmatched legacy columns. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1115,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_secondemail":{"description":"Getter maps PHP truthiness to integer enable/disable. Disabling always updates the flag. Enabling requires an existing stored destination field, or a supplied recognized booking_secondemail_field public field key. A missing/unrecognized destination returns false from the hook. The companion field is conditionally required based on stored account state, not on every request.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"type":"boolean"},"x-edoobox-preconditions":["When no destination is stored, enabling requires booking_secondemail_field in the same data object."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1150,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":693,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_secondemail_field":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. An unknown scalar key clears it; readback is false when empty, a public key when recognized, and null for unmatched legacy columns. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1171,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_offerlist_secondemail_field":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. An unknown scalar key clears it; readback is false when empty, a public key when recognized, and null for unmatched legacy columns. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1233,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"booking_offerlist_cancel_mail":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1268,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_allow_promotions":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1288,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"booking_designation_promotions":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":50,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":1297},"skip_counter":false,"question_deliverables_show":false,"multi_offer_show":false,"multi_offer_child_limit":false,"automatic_deletion":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1367,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"edsupport_access":{"description":"true or string true becomes 1; false, string false, null and empty string become 0; otherwise positive integer-cast values are retained, nonpositive values become 0. The getter does not reject noncanonical types.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[false],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"boolean"},{"type":"integer","minimum":0}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1395,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"payment_confirmation_mail":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1428,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"barcode_type":{"x-edoobox-knownInputs":["pdf417","qrcode","datamatrix"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["pdf417"],"x-edoobox-sourceLine":1437},"barcode_mobile_wallet":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1461,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mobile_wallet_background_color":{"description":"Trims/string-casts; six hexadecimal digits with optional # normalize to uppercase #RRGGBB. Empty or malformed values become '#0068B0'. No rejecting input pattern is applied.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["#12ab34"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1470,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"mobile_wallet_foreground_color":{"description":"Trims/string-casts; six hexadecimal digits with optional # normalize to uppercase #RRGGBB. Empty or malformed values become ''. No rejecting input pattern is applied.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["#12ab34"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1479,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"mobile_wallet_use_offer_image":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1488,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"mobile_wallet_fallback_image_url":{"description":"String-casts, strips HTML tags, trims and byte-truncates to 500 bytes. Despite the field name, no URL syntax or scheme validator is applied.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["https://example.invalid/support"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"mobile_wallet_display_name":{"description":"String-casts, strips HTML tags, trims and byte-truncates to 150 bytes. The response setter only string-casts stored data.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["Example organization"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1506,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"mobile_wallet_support_url":{"description":"String-casts, strips HTML tags, trims and byte-truncates to 500 bytes. Despite the field name, no URL syntax or scheme validator is applied.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["https://example.invalid/support"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1515,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"barcode_ticket":{"x-edoobox-knownInputs":[false,"always","offer"],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":[false],"x-edoobox-sourceLine":1524},"collective_booking_max":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1548,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"collective_booking_identify":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. Unknown keys return null; readback is false when empty, a public key when recognized, and null for unmatched legacy columns. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1563,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"collective_booking_identify_privacy":{"description":"A false-like input clears the setting. A recognized public data field key is mapped to its internal column; internal column names must not be submitted. The clear sentinel is stored as NO; unknown keys store empty text. Readback is false for NO, a public key when recognized, and null otherwise. Arrays/objects are not valid PHP array keys and may produce an error; the canonical shape is a public key or false.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["email"],"x-edoobox-canonicalSchema":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false}]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1586,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"collective_booking_identify_obligatory":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1610,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"collective_booking_master":{"x-edoobox-knownInputs":["manual","all",false],"x-edoobox-normalization":"Switch default normalizes other input values; case labels are not an exhaustive accepted-input enum.","examples":["manual"],"x-edoobox-sourceLine":1673},"security_headers_csp":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1654,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"design_subcategory":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1699,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"legaltext_atc":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1708,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"legaltext_ri":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1717,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"legaltext_pp":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1726,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"attendance_config":{"type":"array","items":{"type":"object","properties":{"status":{"type":["number","string"],"description":"Converted to integer by the callback."},"order":{"type":["number","string"]},"counter":{},"val":{},"color":{},"help":{}},"additionalProperties":true,"required":["status"]},"description":"The converted statuses 0, 1 and 2 must each occur exactly once among processed items. Counter values outside 0..100 become 0. Extra statuses are supported; processing stops after 41 entries or on a missing status.","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":153,"reason":"PUT sorts the status-record array, casts status/order and normalizes counter.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settingsfunc.php","line":208,"reason":"Exactly three required-status occurrences are checked.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":1739,"reason":"Response returns ordered status records as a JSON array.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"language":{"type":"object","properties":{"active":{"type":"array","items":{"type":"string"}}},"additionalProperties":true,"description":"Active language codes in desired order. Unknown codes are ignored. Account language configuration governs availability.","required":["active"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":77,"reason":"PUT expects an active language-code array inside an object.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":1759,"reason":"The resource Structure response placeholder is false; enriched action responses may differ.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"edsubscription":false,"edsubscription_testinguntildate":false,"versions":false,"pdf_engine":{"description":"PHP switch maps input mode 1 to dompdf; mode 2 and other inputs default to mpdf. Readback maps stored dompdf/mpdf to numeric 1/2, with null for unknown stored text.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[2],"x-edoobox-canonicalSchema":{"enum":[1,2]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1821,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"pdf_edit_restriction_password":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":1851,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"pdf_paper_size":{"description":"Requires an exact installed library paper-size key after case folding, before aP trim/storage normalization. Unknown values return strict false from the getter and the final generic joined writer rejects ED80100 before joined persistence. The existing case-insensitive pattern reflects those installed meaningful paper-size strings; the own parent and relation predicates remain independently checked.","x-edoobox-schemaStatus":"source-reviewed-validation","examples":["a4"],"x-edoobox-canonicalSchema":{"enum":["4a0","2a0","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a10","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","b10","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","ra0","ra1","ra2","ra3","ra4","sra0","sra1","sra2","sra3","sra4","letter","half-letter","legal","ledger","tabloid","executive","folio","commercial #10 envelope","catalog #10 1/2 envelope","8.5x11","8.5x14","11x17"]},"type":"string","pattern":"^(?:4[aA]0|2[aA]0|[aA]0|[aA]1|[aA]2|[aA]3|[aA]4|[aA]5|[aA]6|[aA]7|[aA]8|[aA]9|[aA]10|[bB]0|[bB]1|[bB]2|[bB]3|[bB]4|[bB]5|[bB]6|[bB]7|[bB]8|[bB]9|[bB]10|[cC]0|[cC]1|[cC]2|[cC]3|[cC]4|[cC]5|[cC]6|[cC]7|[cC]8|[cC]9|[cC]10|[rR][aA]0|[rR][aA]1|[rR][aA]2|[rR][aA]3|[rR][aA]4|[sS][rR][aA]0|[sS][rR][aA]1|[sS][rR][aA]2|[sS][rR][aA]3|[sS][rR][aA]4|[lL][eE][tT][tT][eE][rR]|[hH][aA][lL][fF]\\-[lL][eE][tT][tT][eE][rR]|[lL][eE][gG][aA][lL]|[lL][eE][dD][gG][eE][rR]|[tT][aA][bB][lL][oO][iI][dD]|[eE][xX][eE][cC][uU][tT][iI][vV][eE]|[fF][oO][lL][iI][oO]|[cC][oO][mM][mM][eE][rR][cC][iI][aA][lL]\\ \\#10\\ [eE][nN][vV][eE][lL][oO][pP][eE]|[cC][aA][tT][aA][lL][oO][gG]\\ \\#10\\ 1/2\\ [eE][nN][vV][eE][lL][oO][pP][eE]|8\\.5[xX]11|8\\.5[xX]14|11[xX]17)$","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1870,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"vendor/dompdf/dompdf/src/Adapter/CPDF.php","line":42,"sha256":"7dca4a096b08c006f12bfccd733068223d92e502b9a625c49bd919373f714001","reason":"Installed Dompdf paper-size key list, matched case-insensitively by the source getter."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Normal fields check strict false; joined fields instead collect getter output at 1793 and execute it at 1827 without that rejection. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"pdf_custom_css":{"description":"Parses CSS, removes non-declaration blocks, keeps selectors starting .custom_ or *, and filters rule value node classes. font-family is retained only when a family is among Pdf::$fonts. Stores compact CSS base64; readback decodes/formats it or returns empty text. Parser errors and PDF renderer prerequisites are not a closed JSON Schema grammar.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[".custom_example { color: #123456; }"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-preconditions":["CSS parser grammar and configured font support are checked by source/parser, not replicated as a misleading JSON pattern."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1892,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/libraries/edoobox/integrations/pdf/Pdf.php","line":16,"sha256":"b286cdc4d20e02601db9acc35b06564357f10378961e72cb8f4e22e64b3a3254","reason":"Allowed PDF font keys used by the CSS normalizer."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"dinaco_client_id":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":1960},"dinaco_client_secret":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":1979},"gtm_mess_id":{"description":"For input longer than five bytes, a non-anchored G-[a-zA-z0-9]{5,20} substring must occur or the value becomes empty. Short values are retained. aP then trims, replaces newlines and truncates. This is not a strict GA4-ID pattern validator.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":["G-EXAMPLE1234"],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1998,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP normalization after the substring test."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"gtm_container_id":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":2024},"meta_html":{"description":"Parses supplied HTML and keeps only meta tags whose name is google-site-verification or facebook-domain-verification. Only their name/content attributes are serialized, then aP truncates to 250 stored characters. This is a lossy sanitizer, not a rejecting HTML schema.","x-edoobox-schemaStatus":"source-reviewed-normalization","examples":[""],"x-edoobox-canonicalSchema":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2044,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"system_attachments_file_naming":{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{},"additionalProperties":{"type":"string"}},"description":"Map keyed by an emailtemplate public ID or ics. Each value maps language codes to file-name text. Filename characters and suffix are normalized; unknown templates/languages are ignored.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2076,"reason":"Input iterates template/ics keys and language-to-name objects; edid occurs on map keys.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":2106,"reason":"Response maps stored keys back to public emailtemplate IDs or ics.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"website_integration":{"enum":["iframe","wordpress"],"x-edoobox-sourceLine":2132},"website_url":{"description":"Accepts empty string or a string accepted by PHP FILTER_VALIDATE_URL. Other values return strict false and API_DATA rejects the update. No restriction to HTTP/HTTPS is imposed by this getter.","x-edoobox-schemaStatus":"source-reviewed-validation","examples":["https://example.invalid"],"x-edoobox-canonicalSchema":{"type":"string"},"type":"string","x-edoobox-preconditions":["PHP FILTER_VALIDATE_URL is the rejecting syntax check; JSON Schema format:uri is not asserted as an exact equivalent."],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Strict false getter rejects invalid URL inputs. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"admin_custom_permissions":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":2172,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"admin_academic_title_use":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":2192,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["id","congress_module","root_category","shortcut","organization_country","organization_currency","skip_counter","question_deliverables_show","multi_offer_show","multi_offer_child_limit","edsubscription","edsubscription_testinguntildate","versions"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Account-Einstellungen ändern","value":{"data":{"organization_name":"Beispielorganisation","firstweekday":1,"automatic_deletion":180}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/{id}/dashboard Startübersicht und Accountstatus abrufen Bündelt das eigene Administratorprofil, SSO-/Zwei-Faktor-Status, berechtigungsabhängige Einstellungen und Stammdaten, Guthaben/Abonnement, Datenschutzstatus und E-Mail-Versandstatus für die Startübersicht. Die Daten beziehen sich auf den angemeldeten Administrator und aktuellen Account; ein id-Pfadselektor wird für diese Zusammenstellung nicht ausgewertet. Enthaltene Settings-Leseaufrufe können Versions-Caches befüllen. - Operation-ID: get_v2_settings_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-settings-id-dashboard-ac00319fb9ca/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_id_dashboard","summary":"Startübersicht und Accountstatus abrufen","description":"Bündelt das eigene Administratorprofil, SSO-/Zwei-Faktor-Status, berechtigungsabhängige Einstellungen und Stammdaten, Guthaben/Abonnement, Datenschutzstatus und E-Mail-Versandstatus für die Startübersicht. Die Daten beziehen sich auf den angemeldeten Administrator und aktuellen Account; ein id-Pfadselektor wird für diese Zusammenstellung nicht ausgewertet. Enthaltene Settings-Leseaufrufe können Versions-Caches befüllen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"Berechtigungsabhängiges Dashboard-Objekt; nicht verfügbare Bereiche bleiben leer bzw. false.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"auth_sso":false,"admin":{"id":"admin_012345abcdef_123456","first_name":"Mara"},"auth_2fa":true,"permission":{},"settings":{},"edtransaction_balance":0,"edtransaction_relationship":false,"edsubscription":{},"GDPR":false,"offerdef":[],"datastructure":[],"paymentsystem":[],"adminconfig":false,"adminconfig_lut":false,"country":[],"mail_sending_reputation_noreply":{},"mail_system_alerts":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"auth_sso":{"type":"boolean"},"admin":{"type":"object","properties":{},"additionalProperties":true},"auth_2fa":{"type":"boolean"},"permission":{"type":["object","array"]},"settings":{"type":["object","array"]},"edtransaction_balance":{"type":"number"},"edtransaction_relationship":{"type":"boolean"},"edsubscription":{"type":["object","array"]},"GDPR":{"type":"boolean"},"offerdef":{},"datastructure":{},"paymentsystem":{},"adminconfig":{},"adminconfig_lut":{},"country":{},"mail_sending_reputation_noreply":{},"mail_system_alerts":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settings.php","line":2406,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings lookup and account configuration reads; no persistence in these branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2418}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id}/GTM Google-Tag-Manager-Kennungen speichern Speichert gemeinsam die Analytics-Messkennung gtm_mess_id und die Containerkennung gtm_container_id des aktuellen Accounts. Der Quellcode prüft beide Kennungen auf passende Muster; die gültige Messkennung enthält G- und 5–20 Buchstaben/Ziffern, die Containerkennung GTM- und 5–10 Großbuchstaben/Ziffern. id dient dem generischen Update-Selektor und wird in der Antwort gespiegelt. - Operation-ID: put_v2_settings_id_GTM - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-gtm-fd5dd5fb66c1/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche account-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche account-ID.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "gtm_mess_id": "G-ABC1234567", "gtm_container_id": "GTM-ABCDE12" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/GTM' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/GTM"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "gtm_mess_id": "G-ABC1234567", "gtm_container_id": "GTM-ABCDE12" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/GTM" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"gtm_mess_id\":\"G-ABC1234567\",\"gtm_container_id\":\"GTM-ABCDE12\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id_GTM","summary":"Google-Tag-Manager-Kennungen speichern","description":"Speichert gemeinsam die Analytics-Messkennung gtm_mess_id und die Containerkennung gtm_container_id des aktuellen Accounts. Der Quellcode prüft beide Kennungen auf passende Muster; die gültige Messkennung enthält G- und 5–20 Buchstaben/Ziffern, die Containerkennung GTM- und 5–10 Großbuchstaben/Ziffern. id dient dem generischen Update-Selektor und wird in der Antwort gespiegelt.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche account-ID.","schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Request-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"gtm_mess_id":{"type":"string"},"gtm_container_id":{"type":"string"}},"additionalProperties":true,"required":["gtm_mess_id","gtm_container_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Google-Tag-Manager-Kennungen speichern","value":{"data":{"gtm_mess_id":"G-ABC1234567","gtm_container_id":"GTM-ABCDE12"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settings.php","line":2338}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id}/legal_text Sprachabhängigen Account-Rechtstext ändern Aktualisiert value eines älteren Account-Rechtstextplatzes über seine öffentliche Rechtstext-ID. resource und language sind unveränderlich; der Text wird beim Speichern auf die native gespeicherte Länge von 35000 begrenzt. Bestehende native Text-/Accountprüfungen bleiben aktiv. - Operation-ID: put_v2_settings_id_legal_text - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-legal-text-a59e29ec9992/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche legaltext-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legaltext_012345abcdef_123456"]},"description":"Öffentliche legaltext-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "value": "Aktualisierter beispielhafter Rechtstext." } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/legal_text' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/legal_text"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "value": "Aktualisierter beispielhafter Rechtstext." } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/legal_text" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"value\":\"Aktualisierter beispielhafter Rechtstext.\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id_legal_text","summary":"Sprachabhängigen Account-Rechtstext ändern","description":"Aktualisiert value eines älteren Account-Rechtstextplatzes über seine öffentliche Rechtstext-ID. resource und language sind unveränderlich; der Text wird beim Speichern auf die native gespeicherte Länge von 35000 begrenzt. Bestehende native Text-/Accountprüfungen bleiben aktiv.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legaltext_012345abcdef_123456"]},"description":"Öffentliche legaltext-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Rechtstext-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"value":{}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Sprachabhängigen Account-Rechtstext ändern","value":{"data":{"value":"Aktualisierter beispielhafter Rechtstext."}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settings.php","line":2346,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settings.php","line":2358}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/settings/{id}/legaltext Versionierbaren Rechtstext löschen Markiert die ausgewählte Rechtstextgruppe als gelöscht. Bei /list muss id im Request stehen; bei der Einzelroute ist id die öffentliche legaltext-ID im Pfad. Dieser Aufruf löscht keine allgemeinen Account-Einstellungen. - Operation-ID: delete_v2_settings_id_legaltext - Dokumentation: /docs/api/v2/operations/delete-v2-settings-id-legaltext-24a82c0cc7d0/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche legaltext-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legaltext_012345abcdef_123456"]},"description":"Öffentliche legaltext-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/legaltext' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/legaltext"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/legaltext" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_settings_id_legaltext","summary":"Versionierbaren Rechtstext löschen","description":"Markiert die ausgewählte Rechtstextgruppe als gelöscht. Bei /list muss id im Request stehen; bei der Einzelroute ist id die öffentliche legaltext-ID im Pfad. Dieser Aufruf löscht keine allgemeinen Account-Einstellungen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legaltext_012345abcdef_123456"]},"description":"Öffentliche legaltext-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche legaltext-ID und delete-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Versionierbaren Rechtstext löschen","value":{}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settings.php","line":2378,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"deleteSettings","line":2385,"branch":{"file":"application/models/v2/Settings.php","line":2390}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/{id}/legaltext Versionierbaren Rechtstext abrufen Liest die Rechtstextgruppe über ihre öffentliche legaltext-ID. name enthält je Sprache Anzeigename und gegebenenfalls die aktuell fixierte Versionsnummer; Inhalte einzelner Entwürfe werden über legalversion abgerufen. - Operation-ID: get_v2_settings_id_legaltext - Dokumentation: /docs/api/v2/operations/get-v2-settings-id-legaltext-cffe75eee2da/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche legaltext-ID. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche legaltext-ID.","schema":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legaltext_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/legaltext' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/legaltext"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/legaltext" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_id_legaltext","summary":"Versionierbaren Rechtstext abrufen","description":"Liest die Rechtstextgruppe über ihre öffentliche legaltext-ID. name enthält je Sprache Anzeigename und gegebenenfalls die aktuell fixierte Versionsnummer; Inhalte einzelner Entwürfe werden über legalversion abgerufen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche legaltext-ID.","schema":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legaltext_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Rechtstextgruppe in generischer v2-Sammlungsdarstellung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"legaltext_012345abcdef_123456":{"id":"legaltext_012345abcdef_123456","name":{"de":{"name":"Teilnahmebedingungen","version":1}}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":["object","array"],"additionalProperties":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings lookup and account configuration reads; no persistence in these branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2429}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id}/legaltext Anzeigenamen eines versionierbaren Rechtstexts ändern Aktualisiert die sprachabhängigen Anzeigenamen einer vorhandenen Rechtstextgruppe. Für bestehende Sprachschlüssel wird name aus {name: ...} übernommen; neue bekannte Sprachen akzeptiert der Hook als Text und initialisiert ihre Versionsnummer mit 0. Fixierte Versionsnummern bestehender Sprachen bleiben erhalten. - Operation-ID: put_v2_settings_id_legaltext - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-legaltext-efb243c5162b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche legaltext-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche legaltext-ID.","schema":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legaltext_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": { "de": { "name": "Aktuelle Teilnahmebedingungen" } } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/legaltext' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/legaltext"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": { "de": { "name": "Aktuelle Teilnahmebedingungen" } } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/legaltext" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":{\"de\":{\"name\":\"Aktuelle Teilnahmebedingungen\"}}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id_legaltext","summary":"Anzeigenamen eines versionierbaren Rechtstexts ändern","description":"Aktualisiert die sprachabhängigen Anzeigenamen einer vorhandenen Rechtstextgruppe. Für bestehende Sprachschlüssel wird name aus {name: ...} übernommen; neue bekannte Sprachen akzeptiert der Hook als Text und initialisiert ihre Versionsnummer mit 0. Fixierte Versionsnummern bestehender Sprachen bleiben erhalten.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche legaltext-ID.","schema":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legaltext_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche legaltext-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"name":{}},"additionalProperties":true,"required":["name"]},{"type":"string"}]},"description":"Bei schon vorhandenen Sprachen {name: ...}; neue Sprachschlüssel akzeptiert der Hook als Zeichenfolge. Nur bekannte Sprachen werden übernommen."}},"additionalProperties":true,"required":["name"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Anzeigenamen eines versionierbaren Rechtstexts ändern","value":{"data":{"name":{"de":{"name":"Aktuelle Teilnahmebedingungen"}}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settings.php","line":2342}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/{id}/legalversion Rechtstextversion abrufen Liest Inhalt und Fixierungsstatus einer Rechtstextversion über ihre öffentliche legalversion-ID. Die Version gehört über legaltext zu einer Rechtstextgruppe im ausgewählten Account. - Operation-ID: get_v2_settings_id_legalversion - Dokumentation: /docs/api/v2/operations/get-v2-settings-id-legalversion-5395cd54ac4a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche legalversion-ID. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche legalversion-ID.","schema":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legalversion_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/legalversion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/legalversion"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/legalversion" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_id_legalversion","summary":"Rechtstextversion abrufen","description":"Liest Inhalt und Fixierungsstatus einer Rechtstextversion über ihre öffentliche legalversion-ID. Die Version gehört über legaltext zu einer Rechtstextgruppe im ausgewählten Account.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche legalversion-ID.","schema":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legalversion_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Rechtstextversion in generischer v2-Sammlungsdarstellung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"legalversion_012345abcdef_123456":{"id":"legalversion_012345abcdef_123456","legaltext":"legaltext_012345abcdef_123456","language":"de","button_text":"Ich akzeptiere","legal_text":"Beispielhafte Teilnahmebedingungen.","version":1,"is_fixed":false,"fixed_by_admin":"admin_012345abcdef_123456","fixed_at":false}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"language":{"type":"string"},"button_text":{"type":"string"},"legal_text":{"type":"string"},"version":{"type":"integer"},"is_fixed":{"type":"boolean"},"fixed_by_admin":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"const":false}]},"fixed_at":{"type":["string","boolean"]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"language":{"type":"string"},"button_text":{"type":"string"},"legal_text":{"type":"string"},"version":{"type":"integer"},"is_fixed":{"type":"boolean"},"fixed_by_admin":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"const":false}]},"fixed_at":{"type":["string","boolean"]}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings lookup and account configuration reads; no persistence in these branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2437}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id}/legalversion Rechtstextentwurf bearbeiten oder fixieren Ändert language, button_text, legal_text und is_fixed eines noch nicht fixierten Entwurfs. Bei wahrheitswertigem is_fixed werden Zeitpunkt und Administrator gesetzt und die aktuelle Versionsnummer der Sprache im Eltern-Rechtstext aktualisiert. Eine bereits fixierte Version ist unveränderlich und liefert already_fixed. legaltext, version und Fixierungsmetadaten sind nicht direkt aktualisierbar. - Operation-ID: put_v2_settings_id_legalversion - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-legalversion-09ab939448b7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche legalversion-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche legalversion-ID.","schema":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legalversion_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "is_fixed": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/legalversion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/legalversion"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "is_fixed": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/legalversion" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"is_fixed\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id_legalversion","summary":"Rechtstextentwurf bearbeiten oder fixieren","description":"Ändert language, button_text, legal_text und is_fixed eines noch nicht fixierten Entwurfs. Bei wahrheitswertigem is_fixed werden Zeitpunkt und Administrator gesetzt und die aktuelle Versionsnummer der Sprache im Eltern-Rechtstext aktualisiert. Eine bereits fixierte Version ist unveränderlich und liefert already_fixed. legaltext, version und Fixierungsmetadaten sind nicht direkt aktualisierbar.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche legalversion-ID.","schema":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["legalversion_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche legalversion-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legalversion_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"language":{"type":"string"},"button_text":{},"legal_text":{},"is_fixed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Rechtstextentwurf bearbeiten oder fixieren","value":{"data":{"is_fixed":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settings.php","line":2350}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id}/mailsystem/edoobox-email edoobox-Mailversand aktivieren Schaltet bei wahrheitswertigem active den Standardversand über edoobox ein und deaktiviert andere Mailanbieter. Ohne aktives active erfolgt keine Umstellung; aktuelle Versandvorfälle werden beim Einschalten aufgelöst. Der Account wird aus der Anmeldung bestimmt; der Pfad-id ist nur der generische Selektor. - Operation-ID: put_v2_settings_id_mailsystem_edoobox_email - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-mailsystem-edoobox-email-869eb7a00652/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche account-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche account-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "active": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/mailsystem/edoobox-email' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/mailsystem/edoobox-email"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "active": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/mailsystem/edoobox-email" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"active\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id_mailsystem_edoobox_email","summary":"edoobox-Mailversand aktivieren","description":"Schaltet bei wahrheitswertigem active den Standardversand über edoobox ein und deaktiviert andere Mailanbieter. Ohne aktives active erfolgt keine Umstellung; aktuelle Versandvorfälle werden beim Einschalten aufgelöst. Der Account wird aus der Anmeldung bestimmt; der Pfad-id ist nur der generische Selektor.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche account-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Account-ID und update; je Anbieter zusätzlich connect_error, Restversuche, Prüfdaten oder disconnected.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"},"connect_error":{"type":"boolean"},"too_many_tries":{"type":"integer"},"connect_error_code":{"type":"object","properties":{},"additionalProperties":true},"disconnected":{"type":"boolean"},"verification":{"type":"string"},"mailjet":{"type":"object","properties":{"dns":{},"webhook":{}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"active":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"edoobox-Mailversand aktivieren","value":{"data":{"active":true}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settingsfunc.php","line":591,"reason":"Settings update dispatch selects mailsystem/provider while current.id is the account v2 ID returned by GETsettings; source supports this exact provider."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settingsfunc.php","line":606}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id}/mailsystem/mailjet Mailjet konfigurieren und prüfen Prüft Mailjet-Zugangsdaten, aktiviert bei erfolgreicher Verbindung Mailjet, speichert Schlüssel und Absenderadresse und ergänzt fehlende Webhooks. Bestehende Anbieter werden deaktiviert; DNS- und Webhookdaten können zurückgegeben werden. Accountweite Versandsperre und begrenzte Verbindungsversuche bleiben maßgeblich. Der Account wird aus der Anmeldung bestimmt; der Pfad-id ist nur der generische Selektor. - Operation-ID: put_v2_settings_id_mailsystem_mailjet - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-mailsystem-mailjet-f22cf05c477a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche account-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche account-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "active": true, "email": "office@example.invalid", "key": "synthetic-mailjet-key", "secret": "synthetic-mailjet-secret" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/mailsystem/mailjet' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/mailsystem/mailjet"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "active": true, "email": "office@example.invalid", "key": "synthetic-mailjet-key", "secret": "synthetic-mailjet-secret" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/mailsystem/mailjet" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"active\":true,\"email\":\"office@example.invalid\",\"key\":\"synthetic-mailjet-key\",\"secret\":\"synthetic-mailjet-secret\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id_mailsystem_mailjet","summary":"Mailjet konfigurieren und prüfen","description":"Prüft Mailjet-Zugangsdaten, aktiviert bei erfolgreicher Verbindung Mailjet, speichert Schlüssel und Absenderadresse und ergänzt fehlende Webhooks. Bestehende Anbieter werden deaktiviert; DNS- und Webhookdaten können zurückgegeben werden. Accountweite Versandsperre und begrenzte Verbindungsversuche bleiben maßgeblich. Der Account wird aus der Anmeldung bestimmt; der Pfad-id ist nur der generische Selektor.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche account-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Account-ID und update; je Anbieter zusätzlich connect_error, Restversuche, Prüfdaten oder disconnected.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"connect_error":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"},"connect_error":{"type":"boolean"},"too_many_tries":{"type":"integer"},"connect_error_code":{"type":"object","properties":{},"additionalProperties":true},"disconnected":{"type":"boolean"},"verification":{"type":"string"},"mailjet":{"type":"object","properties":{"dns":{},"webhook":{}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"active":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"email":{"type":"string"},"key":{"type":"string","writeOnly":true},"secret":{"type":"string","writeOnly":true}},"additionalProperties":true,"required":["active","email","key","secret"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mailjet konfigurieren und prüfen","value":{"data":{"active":true,"email":"office@example.invalid","key":"synthetic-mailjet-key","secret":"synthetic-mailjet-secret"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settingsfunc.php","line":591,"reason":"Settings update dispatch selects mailsystem/provider while current.id is the account v2 ID returned by GETsettings; source supports this exact provider."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settingsfunc.php","line":609}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id}/mailsystem/microsoft Microsoft-Mailversand konfigurieren oder trennen Mit disconnect=true wird die Microsoft-Verbindung ausdrücklich getrennt. Sonst sind email und ein echter boolescher active erforderlich; es muss bereits eine OAuth-Verbindung bestehen. Die Prüfung sendet eine Testmail, speichert bei Erfolg Absender/Aktivierung und deaktiviert bei Aktivierung andere Anbieter. Ein Fehler liefert connect_error statt einer erfolgreichen Umstellung. Der Account wird aus der Anmeldung bestimmt; der Pfad-id ist nur der generische Selektor. - Operation-ID: put_v2_settings_id_mailsystem_microsoft - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-mailsystem-microsoft-47953ccd5e95/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche account-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche account-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "email": "office@example.invalid", "active": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/mailsystem/microsoft' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/mailsystem/microsoft"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "email": "office@example.invalid", "active": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/mailsystem/microsoft" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"email\":\"office@example.invalid\",\"active\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id_mailsystem_microsoft","summary":"Microsoft-Mailversand konfigurieren oder trennen","description":"Mit disconnect=true wird die Microsoft-Verbindung ausdrücklich getrennt. Sonst sind email und ein echter boolescher active erforderlich; es muss bereits eine OAuth-Verbindung bestehen. Die Prüfung sendet eine Testmail, speichert bei Erfolg Absender/Aktivierung und deaktiviert bei Aktivierung andere Anbieter. Ein Fehler liefert connect_error statt einer erfolgreichen Umstellung. Der Account wird aus der Anmeldung bestimmt; der Pfad-id ist nur der generische Selektor.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche account-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Account-ID und update; je Anbieter zusätzlich connect_error, Restversuche, Prüfdaten oder disconnected.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"connect_error":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"},"connect_error":{"type":"boolean"},"too_many_tries":{"type":"integer"},"connect_error_code":{"type":"object","properties":{},"additionalProperties":true},"disconnected":{"type":"boolean"},"verification":{"type":"string"},"mailjet":{"type":"object","properties":{"dns":{},"webhook":{}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"disconnect":{"const":true}},"additionalProperties":true,"required":["disconnect"]},{"type":"object","properties":{"disconnect":{"const":false},"email":{"type":"string","format":"email"},"active":{"type":"boolean"}},"additionalProperties":true,"required":["email","active"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Microsoft-Mailversand konfigurieren oder trennen","value":{"data":{"email":"office@example.invalid","active":true}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settingsfunc.php","line":601,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settingsfunc.php","line":612}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/{id}/mailsystem/smtp SMTP-Versand konfigurieren und prüfen Prüft SMTP-Verbindungsdaten durch Versand einer Testmail und speichert bei Erfolg die Konfiguration als aktives Mailsystem. Ein vorhandenes Passwort kann wiederverwendet werden. Accountweite Versandsperre und begrenzte Verbindungsversuche bleiben maßgeblich. active/authorize werden nativ als Wahrheitswerte normalisiert; port wird zur Ganzzahl konvertiert. Der Account wird aus der Anmeldung bestimmt; der Pfad-id ist nur der generische Selektor. - Operation-ID: put_v2_settings_id_mailsystem_smtp - Dokumentation: /docs/api/v2/operations/put-v2-settings-id-mailsystem-smtp-8c834f5fbae8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche account-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche account-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "active": true, "email": "office@example.invalid", "user": "office@example.invalid", "password": "synthetic-smtp-password", "url": "smtp.example.invalid", "port": 587, "authorize": true, "secure": "tls" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/'"${PATH_ID}"'/mailsystem/smtp' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/{id}/mailsystem/smtp"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "active": true, "email": "office@example.invalid", "user": "office@example.invalid", "password": "synthetic-smtp-password", "url": "smtp.example.invalid", "port": 587, "authorize": true, "secure": "tls" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/{id}/mailsystem/smtp" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"active\":true,\"email\":\"office@example.invalid\",\"user\":\"office@example.invalid\",\"password\":\"synthetic-smtp-password\",\"url\":\"smtp.example.invalid\",\"port\":587,\"authorize\":true,\"secure\":\"tls\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_id_mailsystem_smtp","summary":"SMTP-Versand konfigurieren und prüfen","description":"Prüft SMTP-Verbindungsdaten durch Versand einer Testmail und speichert bei Erfolg die Konfiguration als aktives Mailsystem. Ein vorhandenes Passwort kann wiederverwendet werden. Accountweite Versandsperre und begrenzte Verbindungsversuche bleiben maßgeblich. active/authorize werden nativ als Wahrheitswerte normalisiert; port wird zur Ganzzahl konvertiert. Der Account wird aus der Anmeldung bestimmt; der Pfad-id ist nur der generische Selektor.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID.","examples":["account_012345abcdef_123456"]},"description":"Öffentliche account-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Account-ID und update; je Anbieter zusätzlich connect_error, Restversuche, Prüfdaten oder disconnected.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"connect_error":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"},"connect_error":{"type":"boolean"},"too_many_tries":{"type":"integer"},"connect_error_code":{"type":"object","properties":{},"additionalProperties":true},"disconnected":{"type":"boolean"},"verification":{"type":"string"},"mailjet":{"type":"object","properties":{"dns":{},"webhook":{}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"active":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"email":{"type":"string","format":"email"},"user":{},"password":{"type":"string","writeOnly":true,"description":"Kann entfallen, wenn ein gespeichertes Passwort wiederverwendbar ist."},"url":{},"port":{"type":["number","string"],"description":"Wird zur Ganzzahl konvertiert und muss positiv sein."},"authorize":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"secure":{"type":["string","boolean","null"],"description":"ssl, tls oder ein leerer/false-Wert für unverschlüsselt; andere Werte normalisiert der Legacy-Getter."}},"additionalProperties":true,"required":["active","email","user","url","port","authorize","secure"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"SMTP-Versand konfigurieren und prüfen","value":{"data":{"active":true,"email":"office@example.invalid","user":"office@example.invalid","password":"synthetic-smtp-password","url":"smtp.example.invalid","port":587,"authorize":true,"secure":"tls"}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settingsfunc.php","line":591,"reason":"Settings update dispatch selects mailsystem/provider while current.id is the account v2 ID returned by GETsettings; source supports this exact provider."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settingsfunc.php","line":615}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/accesssystems Zugangssysteme und Prüfprotokoll abrufen Liest die Zugangssystem-Konfiguration des Accounts mit Verbindungen, Zugangspunkten, Regeln, Geräten, Räumen und verfügbaren Präsenzstatus sowie den letzten höchstens 200 Zugangsentscheidungen. Liefert öffentliche v2-IDs und Webhook-URLs; geheime Bearer-Tokens werden nicht ausgegeben. Die Accountmigration für Zugangssysteme muss vorhanden sein. - Operation-ID: get_v2_settings_accesssystems - Dokumentation: /docs/api/v2/operations/get-v2-settings-accesssystems-cd09fc40c281/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/accesssystems' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/accesssystems"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/accesssystems" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_accesssystems","summary":"Zugangssysteme und Prüfprotokoll abrufen","description":"Liest die Zugangssystem-Konfiguration des Accounts mit Verbindungen, Zugangspunkten, Regeln, Geräten, Räumen und verfügbaren Präsenzstatus sowie den letzten höchstens 200 Zugangsentscheidungen. Liefert öffentliche v2-IDs und Webhook-URLs; geheime Bearer-Tokens werden nicht ausgegeben. Die Accountmigration für Zugangssysteme muss vorhanden sein.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Vollständiger Konfigurations-Snapshot mit server_time als Unix-Sekunden.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"connections":[],"points":[],"rules":[],"decisions":[],"rooms":[],"devices":[],"attendance_statuses":[],"server_time":1792620000}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"connections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"},"enabled":{"type":"boolean"},"provider":{"type":"string"},"webhook_path":{"type":"string"},"webhook_url":{"type":"string"},"authentication":{"const":"bearer"},"pairing_until":{"type":"integer"},"pairing_session":{"type":"string"}},"additionalProperties":true}},"points":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accesspoint_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"},"enabled":{"type":"boolean"},"connection":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"attendance_status":{"type":["integer","null"]},"devices":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accessrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"},"enabled":{"type":"boolean"},"point":{"type":"string","pattern":"^accesspoint_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"target_type":{"type":"string"},"target":{"type":"string"},"minutes_before":{"type":"integer"},"minutes_after":{"type":"integer"}},"additionalProperties":true}},"devices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accessdevice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"connection":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"point":{"type":["string","null"]},"device_id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"last_seen_at":{"type":"integer"}},"additionalProperties":true}},"decisions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^accessdecision_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"connection":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"point":{"type":["string","null"]},"user":{"type":["string","null"]},"booking":{"type":["string","null"]},"date":{"type":["string","null"]},"time":{"type":"integer"},"success":{"type":"boolean"},"reason":{"type":"string"},"source":{"type":"string"}},"additionalProperties":true}},"rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"name":{"type":"string"}},"additionalProperties":true}},"attendance_statuses":{"type":"array","items":{"type":"object","properties":{"status":{"type":"integer"},"name":{"type":"string"},"symbol":{"type":"string"}},"additionalProperties":true}},"server_time":{"type":"integer"}},"additionalProperties":true,"required":["connections","points","rules","decisions","rooms","devices","server_time","attendance_statuses"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Accesssystems.php","line":27},"reason":"GET account access-system configuration only; write methods are separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2410}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/accesssystems Zugangssystem einrichten Legt connection, point, rule oder device an; vorhandene id sowie token/pairing/preview sind bei POST verboten. Neue Verbindungen starten unabhängig von enabled deaktiviert und geben den geheimen Token genau einmal zurück. entity wählt den Vertrag. point verlangt eine Verbindung; rule verlangt Zugangspunkt, Ziel und Fünf-Minuten-Zeitfenster. Gerätezuteilungen können vorhandene Geräte deaktivieren; bei Gerätezuweisung nach Erkennung ist die gültige pairing-session erforderlich. Zielobjekte und Statuswerte müssen im Account verfügbar sein. Das comydo-Plugin und die Zugangsmigration müssen vorhanden sein. - Operation-ID: post_v2_settings_accesssystems - Dokumentation: /docs/api/v2/operations/post-v2-settings-accesssystems-2567c4cbfe7e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "entity": "point", "name": "Haupteingang", "enabled": true, "connection": "accessconnection_012345abcdef_123456", "attendance_status": null } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/accesssystems' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/accesssystems"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "entity": "point", "name": "Haupteingang", "enabled": true, "connection": "accessconnection_012345abcdef_123456", "attendance_status": null } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/accesssystems" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"entity\":\"point\",\"name\":\"Haupteingang\",\"enabled\":true,\"connection\":\"accessconnection_012345abcdef_123456\",\"attendance_status\":null}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_accesssystems","summary":"Zugangssystem einrichten","description":"Legt connection, point, rule oder device an; vorhandene id sowie token/pairing/preview sind bei POST verboten. Neue Verbindungen starten unabhängig von enabled deaktiviert und geben den geheimen Token genau einmal zurück. entity wählt den Vertrag. point verlangt eine Verbindung; rule verlangt Zugangspunkt, Ziel und Fünf-Minuten-Zeitfenster. Gerätezuteilungen können vorhandene Geräte deaktivieren; bei Gerätezuweisung nach Erkennung ist die gültige pairing-session erforderlich. Zielobjekte und Statuswerte müssen im Account verfügbar sein. Das comydo-Plugin und die Zugangsmigration müssen vorhanden sein.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"entity-abhängig: öffentliche ID; neue Verbindung mit connection/Token, Tokenrotation mit Token, Pairing mit session/expires_at oder expires_at=0, Vorschau mit success/reason.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"accesspoint_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"connection":{"type":"object","properties":{},"additionalProperties":true},"token":{"type":"string","writeOnly":false,"description":"Geheimer Einmaltoken nur bei Neuerstellung/Rotation."},"session":{"type":"string"},"expires_at":{"type":"integer"},"success":{"type":"boolean"},"reason":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"entity":{"const":"connection"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"provider":{"const":"comydo"},"additional_connection":{"type":"boolean"}},"additionalProperties":true,"required":["entity","name","enabled"]},{"type":"object","properties":{"entity":{"const":"point"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"connection":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"attendance_status":{"type":["integer","null"],"description":"null oder ein aktueller Status aus attendance_statuses."},"devices":{"type":"array","maxItems":20,"uniqueItems":true,"items":{"type":"string","pattern":"^[0-9a-fA-F]{8,20}$"}}},"additionalProperties":true,"required":["entity","name","enabled","connection"]},{"type":"object","properties":{"entity":{"const":"rule"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"point":{"type":"string","pattern":"^accesspoint_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"target_type":{"enum":["offer","room","root","category"]},"target":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}]},"minutes_before":{"type":"integer","minimum":0,"maximum":1440,"multipleOf":5},"minutes_after":{"type":"integer","minimum":0,"maximum":1440,"multipleOf":5}},"additionalProperties":true,"required":["entity","name","enabled","point","target_type","target","minutes_before","minutes_after"]},{"type":"object","properties":{"entity":{"const":"device"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"point":{"type":"string","pattern":"^accesspoint_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"device_id":{"type":"string","pattern":"^[0-9a-fA-F]{8,20}$"}},"additionalProperties":true,"required":["entity","name","enabled","point","device_id"]}],"discriminator":{"propertyName":"entity"}}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zugangssystem einrichten","value":{"data":{"entity":"point","name":"Haupteingang","enabled":true,"connection":"accessconnection_012345abcdef_123456","attendance_status":null}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2263}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/accesssystems Zugangssystem ändern oder Aktion ausführen Ändert eine vorhandene Zugangskonfiguration oder führt token (geheimen Bearer-Token ersetzen), pairing (fünfminütige Geräteerkennung starten/stoppen) oder preview (Entscheidung prüfen und protokollieren) aus. Für jedes entity ist id erforderlich. entity wählt den Vertrag. point verlangt eine Verbindung; rule verlangt Zugangspunkt, Ziel und Fünf-Minuten-Zeitfenster. Gerätezuteilungen können vorhandene Geräte deaktivieren; bei Gerätezuweisung nach Erkennung ist die gültige pairing-session erforderlich. Zielobjekte und Statuswerte müssen im Account verfügbar sein. Das comydo-Plugin und die Zugangsmigration müssen vorhanden sein. - Operation-ID: put_v2_settings_accesssystems - Dokumentation: /docs/api/v2/operations/put-v2-settings-accesssystems-5f10da2d05cc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "entity": "point", "name": "Haupteingang", "enabled": true, "connection": "accessconnection_012345abcdef_123456", "attendance_status": null, "id": "accesspoint_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/accesssystems' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/accesssystems"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "entity": "point", "name": "Haupteingang", "enabled": true, "connection": "accessconnection_012345abcdef_123456", "attendance_status": null, "id": "accesspoint_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/accesssystems" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"entity\":\"point\",\"name\":\"Haupteingang\",\"enabled\":true,\"connection\":\"accessconnection_012345abcdef_123456\",\"attendance_status\":null,\"id\":\"accesspoint_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_accesssystems","summary":"Zugangssystem ändern oder Aktion ausführen","description":"Ändert eine vorhandene Zugangskonfiguration oder führt token (geheimen Bearer-Token ersetzen), pairing (fünfminütige Geräteerkennung starten/stoppen) oder preview (Entscheidung prüfen und protokollieren) aus. Für jedes entity ist id erforderlich. entity wählt den Vertrag. point verlangt eine Verbindung; rule verlangt Zugangspunkt, Ziel und Fünf-Minuten-Zeitfenster. Gerätezuteilungen können vorhandene Geräte deaktivieren; bei Gerätezuweisung nach Erkennung ist die gültige pairing-session erforderlich. Zielobjekte und Statuswerte müssen im Account verfügbar sein. Das comydo-Plugin und die Zugangsmigration müssen vorhanden sein.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"entity-abhängig: öffentliche ID; neue Verbindung mit connection/Token, Tokenrotation mit Token, Pairing mit session/expires_at oder expires_at=0, Vorschau mit success/reason.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"accesspoint_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string"},"connection":{"type":"object","properties":{},"additionalProperties":true},"token":{"type":"string","writeOnly":false,"description":"Geheimer Einmaltoken nur bei Neuerstellung/Rotation."},"session":{"type":"string"},"expires_at":{"type":"integer"},"success":{"type":"boolean"},"reason":{"type":"string"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"entity":{"const":"connection"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"provider":{"const":"comydo"},"additional_connection":{"type":"boolean"},"id":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["entity","name","enabled","id"]},{"type":"object","properties":{"entity":{"const":"point"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"connection":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"attendance_status":{"type":["integer","null"],"description":"null oder ein aktueller Status aus attendance_statuses."},"devices":{"type":"array","maxItems":20,"uniqueItems":true,"items":{"type":"string","pattern":"^[0-9a-fA-F]{8,20}$"}},"id":{"type":"string","pattern":"^accesspoint_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["entity","name","enabled","connection","id"]},{"type":"object","properties":{"entity":{"const":"rule"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"point":{"type":"string","pattern":"^accesspoint_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"target_type":{"enum":["offer","room","root","category"]},"target":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"string","pattern":"^room_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}]},"minutes_before":{"type":"integer","minimum":0,"maximum":1440,"multipleOf":5},"minutes_after":{"type":"integer","minimum":0,"maximum":1440,"multipleOf":5},"id":{"type":"string","pattern":"^accessrule_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["entity","name","enabled","point","target_type","target","minutes_before","minutes_after","id"]},{"type":"object","properties":{"entity":{"const":"device"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"point":{"type":"string","pattern":"^accesspoint_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"session":{"type":"string"},"id":{"type":"string","pattern":"^accessdevice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["entity","name","enabled","point","id"]},{"type":"object","properties":{"entity":{"const":"token"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"id":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["entity","id"]},{"type":"object","properties":{"entity":{"const":"pairing"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"action":{"enum":["start","stop"]},"id":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["entity","action","id"]},{"type":"object","properties":{"entity":{"const":"preview"},"name":{"type":"string","minLength":1,"maxLength":120,"description":"Nicht leer nach trim; Steuerzeichen werden abgelehnt."},"enabled":{"type":"boolean"},"device_id":{"type":"string","pattern":"^[0-9a-fA-F]{8,20}$"},"content":{"type":"string","maxLength":96},"id":{"type":"string","pattern":"^accessconnection_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["entity","device_id","content","id"]}],"discriminator":{"propertyName":"entity"}}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zugangssystem ändern oder Aktion ausführen","value":{"data":{"entity":"point","name":"Haupteingang","enabled":true,"connection":"accessconnection_012345abcdef_123456","attendance_status":null,"id":"accesspoint_012345abcdef_123456"}}}}}}},"x-edoobox-sensitive-response-paths":["/data/token"],"x-edoobox-user-handoff":{"sensitivePaths":["/data/token"],"oneTime":true},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settings.php","line":2326}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/legaltext Versionierbaren Rechtstext anlegen Legt eine Rechtstextgruppe mit sprachabhängigen Anzeigenamen an. name ist das Erkennungsfeld des Einzel-Einfügepfads; seine Schlüssel sind bekannte Sprachcodes und die Werte enthalten name. Der eigentliche Inhalt wird anschließend als legalversion angelegt. - Operation-ID: post_v2_settings_legaltext - Dokumentation: /docs/api/v2/operations/post-v2-settings-legaltext-b4940ece288b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": { "de": { "name": "Teilnahmebedingungen" }, "en": { "name": "Participation terms" } } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/legaltext' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/legaltext"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": { "de": { "name": "Teilnahmebedingungen" }, "en": { "name": "Participation terms" } } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/legaltext" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":{\"de\":{\"name\":\"Teilnahmebedingungen\"},\"en\":{\"name\":\"Participation terms\"}}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_legaltext","summary":"Versionierbaren Rechtstext anlegen","description":"Legt eine Rechtstextgruppe mit sprachabhängigen Anzeigenamen an. name ist das Erkennungsfeld des Einzel-Einfügepfads; seine Schlüssel sind bekannte Sprachcodes und die Werte enthalten name. Der eigentliche Inhalt wird anschließend als legalversion angelegt.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche legaltext-ID und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"name":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{}},"additionalProperties":true,"required":["name"]},"description":"Sprachcode -> {name: Anzeigename}; nur bekannte Sprachen werden übernommen."}},"additionalProperties":true,"required":["name"]},{"type":"array","items":{"type":"object","properties":{"name":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{}},"additionalProperties":true,"required":["name"]},"description":"Sprachcode -> {name: Anzeigename}; nur bekannte Sprachen werden übernommen."}},"additionalProperties":true,"required":["name"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Versionierbaren Rechtstext anlegen","value":{"data":{"name":{"de":{"name":"Teilnahmebedingungen"},"en":{"name":"Participation terms"}}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2272}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/legalversion Entwurf einer Rechtstextversion anlegen Legt zu legaltext und language einen neuen Entwurf an. Beide Felder sind erforderlich; version wird als nächste Nummer je Rechtstext und Sprache berechnet. is_fixed wird beim Anlegen stets zurückgesetzt, fixed_by_admin/fixed_at werden serverseitig gesetzt. button_text und legal_text enthalten den später anzuzeigenden Inhalt. - Operation-ID: post_v2_settings_legalversion - Dokumentation: /docs/api/v2/operations/post-v2-settings-legalversion-909338d12448/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "legaltext": "legaltext_012345abcdef_123456", "language": "de", "button_text": "Ich akzeptiere", "legal_text": "Beispielhafte Teilnahmebedingungen." } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/legalversion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/legalversion"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "legaltext": "legaltext_012345abcdef_123456", "language": "de", "button_text": "Ich akzeptiere", "legal_text": "Beispielhafte Teilnahmebedingungen." } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/legalversion" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"legaltext\":\"legaltext_012345abcdef_123456\",\"language\":\"de\",\"button_text\":\"Ich akzeptiere\",\"legal_text\":\"Beispielhafte Teilnahmebedingungen.\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_legalversion","summary":"Entwurf einer Rechtstextversion anlegen","description":"Legt zu legaltext und language einen neuen Entwurf an. Beide Felder sind erforderlich; version wird als nächste Nummer je Rechtstext und Sprache berechnet. is_fixed wird beim Anlegen stets zurückgesetzt, fixed_by_admin/fixed_at werden serverseitig gesetzt. button_text und legal_text enthalten den später anzuzeigenden Inhalt.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche legalversion-ID und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legalversion_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"language":{"type":"string"},"button_text":{},"legal_text":{},"version":{},"is_fixed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"fixed_by_admin":{},"fixed_at":{}},"additionalProperties":true,"required":["legaltext","language"]},{"type":"array","items":{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"language":{"type":"string"},"button_text":{},"legal_text":{},"version":{},"is_fixed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"fixed_by_admin":{},"fixed_at":{}},"additionalProperties":true,"required":["legaltext","language"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Entwurf einer Rechtstextversion anlegen","value":{"data":{"legaltext":"legaltext_012345abcdef_123456","language":"de","button_text":"Ich akzeptiere","legal_text":"Beispielhafte Teilnahmebedingungen."}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2275}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list Account-Einstellungen abrufen Liest die zentralen Einstellungen des ausgewählten Accounts sowie Sprachen, Archiv-/Papierkorb-IDs, Währungen, Abonnement- und Versionsinformationen. Die Antwort reduziert die generische Auswahl auf das erste gefundene Einstellungsobjekt und ergänzt Account-Metadaten. Bei Bedarf werden Versions-Caches für Admin-Konfigurationsvorlagen und Mehrwertsteuer befüllt. Keine Zugangsdaten aus der Antwort veröffentlichen. - Operation-ID: get_v2_settings_list - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-b9082cd2c53c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list","summary":"Account-Einstellungen abrufen","description":"Liest die zentralen Einstellungen des ausgewählten Accounts sowie Sprachen, Archiv-/Papierkorb-IDs, Währungen, Abonnement- und Versionsinformationen. Die Antwort reduziert die generische Auswahl auf das erste gefundene Einstellungsobjekt und ergänzt Account-Metadaten. Bei Bedarf werden Versions-Caches für Admin-Konfigurationsvorlagen und Mehrwertsteuer befüllt. Keine Zugangsdaten aus der Antwort veröffentlichen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Ein einzelnes Einstellungsobjekt mit zusätzlichen Account-Metadaten, auch bei /list.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","organization_name":"Beispielorganisation","organization_language":"de","root_archive":"category_012345abcdef_123456","root_trash":"category_012345abcdef_123456","language":{"languages":["Deutsch","English"],"active":["de"],"inactive":["en"]},"user_currencys":["CHF"],"account_billing_recipient_admin_id":false},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"firstweekday":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":44,"type":"integer"},"format_date":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":53,"type":"integer"},"format_time":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":62,"type":"integer"},"format_calendar_grid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":71,"type":"integer"},"format_timezone":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":5,"reason":"PUT consumes a zone-name string instead of the integer get placeholder.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":85,"reason":"Response returns the account timezone zone_name.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"congress_module":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":102,"type":"boolean"},"format_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":112,"type":"integer"},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"root_category":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":130,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["rootcategory_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"shortcut":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":140,"type":"string"},"organization_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":150,"type":"string"},"organization_contactperson":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"organization_street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168,"type":"string"},"organization_postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":177,"type":"string"},"organization_town":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":186,"type":"string"},"organization_country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":195,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["organizationcountry_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"organization_language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":205,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/langedoobox_helper.php","line":731,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangIDByName returns configured entry, with fallback for unknown names."},{"file":"application/helpers/langedoobox_helper.php","line":566,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","reason":"edLangById converts internal entries back to public language keys."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_phone":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":214,"type":"string"},"organization_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":223,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP text storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_email":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":57,"reason":"PUT callback validates the trimmed email syntax, checks raw and encoded lengths, and requires an exact aP/aQ identity before returning storage. False is rejected by generic update as ED90100 before a write.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","lineEnd":75},{"file":"application/models/v2/Settings.php","line":237,"reason":"Incoming model getter is bounded to200 characters; generic PUT uses the dedicated validation hook; response unescapes stored text.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","lineEnd":246},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"organization_currency":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":247,"type":"string"},"billingaddress_organization":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":257,"type":"string"},"billingaddress_user":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":266,"type":"string"},"billingaddress_street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":275,"type":"string"},"billingaddress_postcode_town":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":284,"type":"string"},"billingaddress_phone":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":293,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Actual aP string-only/truncating storage normalization."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"billingaddress_email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":303,"type":"string"},"billingaddress_status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":312,"type":"boolean"},"user_duplicate_config":{"type":"object","required":["version","enabled","candidate_threshold","auto_merge_threshold","survivor_strategy","max_merge_users","rules","rules_hash","matching_rules_hash"],"properties":{"version":{"const":2},"enabled":{"type":"boolean"},"candidate_threshold":{"type":"integer","minimum":0,"maximum":100},"auto_merge_threshold":{"type":["integer","null"],"minimum":1,"maximum":100},"survivor_strategy":{"enum":["newest","oldest"]},"max_merge_users":{"type":"integer","minimum":2,"maximum":10},"rules":{"type":"array","items":{"type":"object","required":["field","enabled","required","weight"],"properties":{"field":{"enum":["gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},"enabled":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer","minimum":1,"maximum":10}},"additionalProperties":false}},"rules_hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"matching_rules_hash":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":321,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Userduplicatefunc.php","line":28,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Actual normalizeConfigForSave preserves unchanged rule hashes and normalizes current state."},{"file":"application/models/v2/Userduplicatefunc.php","line":40,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Full known config defaults, coercion, clamping, hash/version semantics."},{"file":"application/models/v2/Userduplicatefunc.php","line":648,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Rule filtering, uniqueness and weight normalization."},{"file":"application/models/v2/Userduplicatefunc.php","line":968,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Allowed public data-field selectors for duplicate matching."},{"file":"application/models/v2/Userduplicatefunc.php","line":488,"sha256":"10da9ce285a01836c403fec784c04775e7713b8f0c61d49c21de38cf3f8ea157","reason":"Changed matching rules invalidate derived duplicate data, not only the stored JSON."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"admin_avatar_mode":{"enum":["face","symbol"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":344,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_recipient_type":{"enum":["organization","billing_address","admin"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":363,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_recipient_email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":390,"type":"string"},"account_billing_recipient_admin_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_(?:-35|[1-9][0-9]*)$","examples":["admin_012345abcdef_123456"],"description":"Canonical public admin v2 ID issued in this account; server validates the account-bound checksum and rejects raw IDs."},{"const":false,"x-edoobox-id-kind":"empty-relation"}],"description":"Billing administrator as a public admin v2 ID; false when unset. Internal database primary keys are never serialized.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":421,"reason":"The corrected inbound getter accepts a canonical tenant-bound admin v2 reference and normalizes explicit clear sentinels; positive raw primary keys are rejected.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":437,"reason":"The outbound setter encodes a positive database admin key as a public v2 ID and emits false for zero.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/API_DATA.php","line":1853,"reason":"Join updates call the field getter before persisting; response serialization calls the field setter at line1068. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"ba623ac262f6f53eec7d494cd53d3c45f91f0eaa6e0d5757b869d71df6423aa5"}]},"account_billing_auto_charge_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":440,"type":"boolean"},"account_billing_topup_enabled":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":459,"type":"boolean"},"account_billing_topup_threshold":{"type":"number","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":478,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"account_billing_topup_amount":{"type":"number","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"internal_all":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":516,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":23,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"notification_minimummaximum":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":526,"type":"boolean"},"notification_booking":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":535,"type":"boolean"},"vat_included":{"enum":["exclusive","including"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":544,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"vat_reverse_charge_texts":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"array","maxItems":0},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":553,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"Each recognized text is normalized by aP."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_sort":{"enum":["date","manual"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":580,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_func_1":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":589,"type":"boolean"},"offer_func_1_status":{"enum":[3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":598,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_archiving":{"enum":[1,2,3,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":607,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"offer_archiving_reference_date":{"enum":["start","visibility"],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":634,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"news_and_tricks":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":644,"type":"boolean"},"booking_create_admin":{"enum":[1,2,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":653,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_userlogin":{"enum":[1,2,3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":686,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_login":{"enum":[1,2,3,null],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":713,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_login_global_users":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":738,"type":"boolean"},"booking_cancellation":{"enum":[1,2,3,4],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":757,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cancellation_admin_confirmation_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":784,"type":"boolean"},"booking_reservation":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":803,"type":"boolean"},"booking_paymentpage":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":812,"type":"boolean"},"booking_sendinfo":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":822,"type":"boolean"},"booking_label_notifications":{"type":"array","items":{"type":"object","required":["label","email","emailtemplate"],"properties":{"label":{"type":"string","minLength":1,"maxLength":15},"email":{"type":"string"},"emailtemplate":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$"}},"additionalProperties":false},"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":831,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3060,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Actual rule filtering and per-account template lookup."},{"file":"application/models/v2/Settings.php","line":3105,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Response filter emits typed public emailtemplate IDs."},{"file":"application/models/v2/Settings.php","line":3159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Template must belong to the account and be an email template, not PDF."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_send_pdf":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":850,"type":"boolean"},"booking_send_ics":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":859,"type":"boolean"},"booking_reminder":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":878,"type":"boolean"},"booking_reminder_hours":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":887,"type":"integer"},"booking_visitconfirmation":{"enum":[1,2,false]},"booking_number":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":937,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":288,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual hook range check and tenant-scoped uniqueness query."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_percent":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":948,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_notvisitmail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":975,"type":"boolean"},"booking_cert_senddelayhours":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":996,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_validation_url":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1024,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":40,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual field hook handles loose truthiness and subscription verification."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_attachpdf":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1046,"type":"boolean"},"booking_cert_attachpdfpercent":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1206,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_secondemail":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1094,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":641,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_cert_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1115,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_secondemail":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1150,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settingsfunc.php","line":693,"sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f","reason":"Actual conditional second-email flag hook; getter called explicitly and destination checked against stored settings."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1171,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_offerlist_secondemail_field":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1233,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"booking_offerlist_cancel_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1268,"type":"boolean"},"booking_allow_promotions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1288,"type":"boolean"},"booking_designation_promotions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1297,"type":"string"},"skip_counter":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1306,"type":"integer"},"question_deliverables_show":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1316,"type":"boolean"},"multi_offer_show":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1336,"type":"boolean"},"multi_offer_child_limit":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1346,"type":"integer"},"automatic_deletion":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1367},"edsupport_access":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1395,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"payment_confirmation_mail":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1428,"type":"boolean"},"barcode_type":{"enum":["pdf417","qrcode","datamatrix"]},"barcode_mobile_wallet":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1461,"type":"boolean"},"mobile_wallet_background_color":{"type":"string","pattern":"^#[0-9A-F]{6}$","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1470,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_foreground_color":{"type":"string","pattern":"^(?:#[0-9A-F]{6})?$","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1479,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Settings.php","line":3181,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Shared color normalizer returns uppercase hex or the field-specific fallback."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_use_offer_image":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1488,"type":"boolean"},"mobile_wallet_fallback_image_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1497,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_display_name":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1506,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"mobile_wallet_support_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1515,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"barcode_ticket":{"enum":[false,"always","offer"]},"collective_booking_max":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1548,"type":"integer"},"collective_booking_identify":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1563,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"collective_booking_identify_privacy":{"anyOf":[{"type":"string","enum":["status","gender","first_name","last_name","street","postcode","location","country","language","phone_number","email","company","data_1","data_2","data_3","data_4","data_5","data_6","data_7","data_8","data_9","data_10","data_11","data_12","data_13","data_14","data_15","data_16","data_17","data_18","data_19","data_20","data_21","data_22","data_23","data_24","data_25","data_26","data_27","data_28","data_29","data_30"]},{"const":false},{"type":"null"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1586,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/Datastructure.php","line":20,"sha256":"256cfd2c1002e4708b342b6649fa6bfda780273306c30ae2a43d424fd0e2e6a8","reason":"Public field-key to storage-column map; this selector is not a database record ID."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"collective_booking_identify_obligatory":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1610,"type":"boolean"},"collective_booking_master":{"enum":["manual","all",false]},"security_headers_csp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1654,"type":"boolean"},"design_subcategory":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1699,"type":"boolean"},"legaltext_atc":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1708,"type":"boolean"},"legaltext_ri":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1717,"type":"boolean"},"legaltext_pp":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1726,"type":"boolean"},"attendance_config":{"type":"array","items":{"type":"object","properties":{"status":{"type":["integer","string"],"description":"The key is derived with an integer cast; stored status representation is preserved in the record."},"order":{"type":"integer"},"counter":{},"val":{},"color":{},"help":{},"lock":{"type":"boolean"}},"additionalProperties":true,"required":["status","order"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Settingsfunc.php","line":153,"reason":"PUT sorts the status-record array, casts status/order and normalizes counter.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settingsfunc.php","line":208,"reason":"Exactly three required-status occurrences are checked.","sha256":"7a289d246c7c6b70fd88c739a308a883f0f983913cc06f6e04b160792aef3c4f"},{"file":"application/models/v2/Settings.php","line":1739,"reason":"Response returns ordered status records as a JSON array.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"language":{"type":"object","properties":{"languages":{"type":"array","items":{"type":"string"}},"active":{"type":["object","array"]},"inactive":{"type":["object","array"]}},"additionalProperties":true},"edsubscription":{"type":"string","pattern":"^edsubscription_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"edsubscription_testinguntildate":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1786},"versions":{"type":"object","properties":{},"additionalProperties":true},"pdf_engine":{"enum":[1,2,null],"x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1821,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"pdf_edit_restriction_password":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1851,"type":"boolean"},"pdf_paper_size":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1870,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"vendor/dompdf/dompdf/src/Adapter/CPDF.php","line":42,"sha256":"7dca4a096b08c006f12bfccd733068223d92e502b9a625c49bd919373f714001","reason":"Installed Dompdf paper-size key list, matched case-insensitively by the source getter."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Normal fields check strict false; joined fields instead collect getter output at 1793 and execute it at 1827 without that rejection. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"pdf_custom_css":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1892,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/libraries/edoobox/integrations/pdf/Pdf.php","line":16,"sha256":"b286cdc4d20e02601db9acc35b06564357f10378961e72cb8f4e22e64b3a3254","reason":"Allowed PDF font keys used by the CSS normalizer."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"dinaco_client_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1960,"type":"string"},"dinaco_client_secret":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":1979,"x-edoobox-sensitive":true},"gtm_mess_id":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":1998,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","reason":"aP normalization after the substring test."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"gtm_container_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2024,"type":"string"},"meta_html":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2044,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"system_attachments_file_naming":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{},"additionalProperties":{"type":"string"}}},{"type":"array","maxItems":0}],"description":"Associative map, or [] when there are no naming rules.","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2076,"reason":"Input iterates template/ics keys and language-to-name objects; edid occurs on map keys.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"application/models/v2/Settings.php","line":2106,"reason":"Response maps stored keys back to public emailtemplate IDs or ics.","sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1"},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"website_integration":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2132},"website_url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Settings.php","line":2159,"sha256":"431e6774611828ce07fb932ead801e3b2e3b7a15214e5d9498487b545c0d73f1","reason":"Structure input getter, response setter and update flags."},{"file":"application/models/v2/API_DATA.php","line":1853,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Strict false getter rejects invalid URL inputs. Final joined writer rejects strict-false getters and unresolved/multi-join metadata before writes, anchors the joined row to the own parent (including account-key parents), and preserves constant join.where constraints as additional AND predicates. Join-only writes verify the own existing parent. Generic simple-owner hard/soft DELETE now verifies the own existing parent before hooks and repeats FID in DML; missing optional hook data no longer warns. Read, insert, signed authority and GET-effect classifications are unchanged."},{"file":"docs/openapi/reviews/settings-fields.json","line":1,"reason":"Field-level Settings source review and runtime limitations; source-executing offline regressions are separate evidence.","sha256":"be8942fdca4dbcf16a5c5767f778b5644e53284101fa8aa6d00d8f1de7cf3df9"}]},"admin_custom_permissions":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2172,"type":"boolean"},"admin_academic_title_use":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":2192,"type":"boolean"},"root_archive":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"root_trash":{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"user_currencys":{"type":"array","items":{"type":"string"}},"stripe_public_key":{"type":["string","boolean"]}},"additionalProperties":true}],"description":"Einzelnes Account-Einstellungsobjekt; auch die Listenroute reduziert auf das erste gefundene Objekt."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-sensitiveResponseFields":["dinaco_client_secret"],"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list/automatic_deletion Vorschau der automatischen Löschung abrufen Zeigt Teilnehmer-/Buchungs-/Angebotskombinationen für die automatische Löschprüfung und gegebenenfalls eine spätere Buchung, die das Löschen blockiert. Der Aufruf führt nur die Vorschau aus und löscht keine Datensätze. limit steuert den betrachteten Teilnehmerausschnitt; fields/filter/order/response_type werden nicht ausgewertet. - Operation-ID: get_v2_settings_list_automatic_deletion - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-automatic-deletion-56416865449d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list/automatic_deletion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/automatic_deletion"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/automatic_deletion" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list_automatic_deletion","summary":"Vorschau der automatischen Löschung abrufen","description":"Zeigt Teilnehmer-/Buchungs-/Angebotskombinationen für die automatische Löschprüfung und gegebenenfalls eine spätere Buchung, die das Löschen blockiert. Der Aufruf führt nur die Vorschau aus und löscht keine Datensätze. limit steuert den betrachteten Teilnehmerausschnitt; fields/filter/order/response_type werden nicht ausgewertet.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Liste geprüfter Zuordnungen; blocked ist false oder ein Objekt mit späterer blockierender Buchung.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"user":"user_012345abcdef_123456","booking":"booking_012345abcdef_123456","offer":"offer_012345abcdef_123456","date_end_displayend":"2026-04-01T18:00:00+02:00","blocked":false}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"date_end_displayend":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."},"blocked":{"anyOf":[{"const":false},{"type":"object","properties":{"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$"},{"const":"booking_nan_0"}],"description":"Öffentliche Buchungs-ID; die aktuelle Vorschau kann wegen eines abweichenden SQL-Alias beim blockierenden Buchungsfeld den Legacy-Sentinel booking_nan_0 liefern."},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"date_end_displayend":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."}},"additionalProperties":true}]}},"additionalProperties":true}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings lookup and account configuration reads; no persistence in these branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":["Bekannte Quellcodeabweichung: SELECT liefert BlockedByBookingBID, outputRowDataDelete liest BlockedByBookingID; daher ist blocked.booking nicht zuverlässig verwendbar und kann booking_nan_0 enthalten."]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2454}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list/dashboard Startübersicht und Accountstatus abrufen Bündelt das eigene Administratorprofil, SSO-/Zwei-Faktor-Status, berechtigungsabhängige Einstellungen und Stammdaten, Guthaben/Abonnement, Datenschutzstatus und E-Mail-Versandstatus für die Startübersicht. Die Daten beziehen sich auf den angemeldeten Administrator und aktuellen Account; ein id-Pfadselektor wird für diese Zusammenstellung nicht ausgewertet. Enthaltene Settings-Leseaufrufe können Versions-Caches befüllen. - Operation-ID: get_v2_settings_list_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-dashboard-e952be22c456/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/dashboard"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/dashboard" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list_dashboard","summary":"Startübersicht und Accountstatus abrufen","description":"Bündelt das eigene Administratorprofil, SSO-/Zwei-Faktor-Status, berechtigungsabhängige Einstellungen und Stammdaten, Guthaben/Abonnement, Datenschutzstatus und E-Mail-Versandstatus für die Startübersicht. Die Daten beziehen sich auf den angemeldeten Administrator und aktuellen Account; ein id-Pfadselektor wird für diese Zusammenstellung nicht ausgewertet. Enthaltene Settings-Leseaufrufe können Versions-Caches befüllen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Berechtigungsabhängiges Dashboard-Objekt; nicht verfügbare Bereiche bleiben leer bzw. false.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"auth_sso":false,"admin":{"id":"admin_012345abcdef_123456","first_name":"Mara"},"auth_2fa":true,"permission":{},"settings":{},"edtransaction_balance":0,"edtransaction_relationship":false,"edsubscription":{},"GDPR":false,"offerdef":[],"datastructure":[],"paymentsystem":[],"adminconfig":false,"adminconfig_lut":false,"country":[],"mail_sending_reputation_noreply":{},"mail_system_alerts":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"auth_sso":{"type":"boolean"},"admin":{"type":"object","properties":{},"additionalProperties":true},"auth_2fa":{"type":"boolean"},"permission":{"type":["object","array"]},"settings":{"type":["object","array"]},"edtransaction_balance":{"type":"number"},"edtransaction_relationship":{"type":"boolean"},"edsubscription":{"type":["object","array"]},"GDPR":{"type":"boolean"},"offerdef":{},"datastructure":{},"paymentsystem":{},"adminconfig":{},"adminconfig_lut":{},"country":{},"mail_sending_reputation_noreply":{},"mail_system_alerts":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings lookup and account configuration reads; no persistence in these branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"maintenance","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2418}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list/DSG Gespeicherte DSG-Vertragsvorgänge abrufen Liest die bisherigen DSG-Vorgänge des Accounts mit Vertragsversion, zugehörigem Administrator, Unterschriftsdaten und Signable-Referenz. Dient zur Anzeige des Vertrags-/Unterzeichnungsstatus; personenbezogene Signaturdaten vertraulich behandeln. - Operation-ID: get_v2_settings_list_DSG - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-dsg-d47d01790c3f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | segment3 | query | nein | Additional query input read by getSettings; action-specific type and validation are defined by the source. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"segment3","in":"query","required":false,"description":"Additional query input read by getSettings; action-specific type and validation are defined by the source.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list/DSG' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/DSG"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/DSG" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list_DSG","summary":"Gespeicherte DSG-Vertragsvorgänge abrufen","description":"Liest die bisherigen DSG-Vorgänge des Accounts mit Vertragsversion, zugehörigem Administrator, Unterschriftsdaten und Signable-Referenz. Dient zur Anzeige des Vertrags-/Unterzeichnungsstatus; personenbezogene Signaturdaten vertraulich behandeln.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"segment3","in":"query","required":false,"description":"Additional query input read by getSettings; action-specific type and validation are defined by the source.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generische Sammlung von Vertragsvorgängen; Datumsdarstellung folgt dQ.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"dsg_012345abcdef_123456":{"id":"dsg_012345abcdef_123456","admin":"admin_012345abcdef_123456","contract_version":"Beispielversion","signing_date":"2026-10-22T10:00:00+02:00","signing_ip":"192.0.2.10","signing_name":"Mara Muster","contract_signable":"synthetic-contract-reference"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^dsg_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"contract_version":{"type":"string"},"signing_date":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."},"signing_ip":{"type":"string"},"signing_name":{"type":"string"},"contract_signable":{"type":"string"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^dsg_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"contract_version":{"type":"string"},"signing_date":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."},"signing_ip":{"type":"string"},"signing_name":{"type":"string"},"contract_signable":{"type":"string"}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2697},"reason":"Privacy configuration reads through GET_data; POST actions are separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2426}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/list/DSG DSG-Vertragsvorgang starten Erstellt einen Signable-Vertragsvorgang und sendet dem angemeldeten Administrator den Link zur Unterzeichnung des Schweizer Datenschutzvertrags. Namen und Empfänger stammen aus dem aktuellen Account-/Administratorprofil. Der Aufruf speichert einen Vertragsvorgang und kann externen E-Mail-Versand auslösen. - Operation-ID: post_v2_settings_list_DSG - Dokumentation: /docs/api/v2/operations/post-v2-settings-list-dsg-dc92edfb3463/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/list/DSG' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/DSG"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": {} }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/DSG" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_list_DSG","summary":"DSG-Vertragsvorgang starten","description":"Erstellt einen Signable-Vertragsvorgang und sendet dem angemeldeten Administrator den Link zur Unterzeichnung des Schweizer Datenschutzvertrags. Namen und Empfänger stammen aus dem aktuellen Account-/Administratorprofil. Der Aufruf speichert einen Vertragsvorgang und kann externen E-Mail-Versand auslösen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Vertragsvorgangs-ID und insert-Ergebnis; der Versand bestätigt noch keine Unterschrift.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"dsg_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^dsg_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^dsg_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"DSG-Vertragsvorgang starten","value":{"data":{}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2308}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list/GDPR Gespeicherte GDPR-Vertragsvorgänge abrufen Liest die bisherigen GDPR-Vorgänge des Accounts mit Vertragsversion, zugehörigem Administrator, Unterschriftsdaten und Signable-Referenz. Dient zur Anzeige des Vertrags-/Unterzeichnungsstatus; personenbezogene Signaturdaten vertraulich behandeln. - Operation-ID: get_v2_settings_list_GDPR - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-gdpr-b71abfeeaf69/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | segment3 | query | nein | Additional query input read by getSettings; action-specific type and validation are defined by the source. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"segment3","in":"query","required":false,"description":"Additional query input read by getSettings; action-specific type and validation are defined by the source.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list/GDPR' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/GDPR"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/GDPR" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list_GDPR","summary":"Gespeicherte GDPR-Vertragsvorgänge abrufen","description":"Liest die bisherigen GDPR-Vorgänge des Accounts mit Vertragsversion, zugehörigem Administrator, Unterschriftsdaten und Signable-Referenz. Dient zur Anzeige des Vertrags-/Unterzeichnungsstatus; personenbezogene Signaturdaten vertraulich behandeln.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"settings_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"segment3","in":"query","required":false,"description":"Additional query input read by getSettings; action-specific type and validation are defined by the source.","schema":{"examples":["example-value"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generische Sammlung von Vertragsvorgängen; Datumsdarstellung folgt dQ.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"gdpr_012345abcdef_123456":{"id":"gdpr_012345abcdef_123456","admin":"admin_012345abcdef_123456","contract_version":"Beispielversion","signing_date":"2026-10-22T10:00:00+02:00","signing_ip":"192.0.2.10","signing_name":"Mara Muster","contract_signable":"synthetic-contract-reference"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^gdpr_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"contract_version":{"type":"string"},"signing_date":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."},"signing_ip":{"type":"string"},"signing_name":{"type":"string"},"contract_signable":{"type":"string"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^gdpr_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"contract_version":{"type":"string"},"signing_date":{"type":["string","boolean"],"description":"ISO-8601-Datum oder false bei fehlendem/nicht darstellbarem Datum."},"signing_ip":{"type":"string"},"signing_name":{"type":"string"},"contract_signable":{"type":"string"}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2697},"reason":"Privacy configuration reads through GET_data; POST actions are separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2424}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/list/GDPR GDPR-Vertragsvorgang starten Bei eu-customer=false wird ein Nicht-EU-Vermerk gespeichert; bei wahrheitswertigem eu-customer wird ein Signable-Vertrag zur Unterzeichnung an den angemeldeten Administrator gesendet. Namen und Empfänger stammen aus dem aktuellen Account-/Administratorprofil. Der Aufruf speichert einen Vertragsvorgang und kann externen E-Mail-Versand auslösen. - Operation-ID: post_v2_settings_list_GDPR - Dokumentation: /docs/api/v2/operations/post-v2-settings-list-gdpr-edef671c115a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "eu-customer": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/list/GDPR' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/GDPR"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "eu-customer": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/GDPR" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"eu-customer\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_list_GDPR","summary":"GDPR-Vertragsvorgang starten","description":"Bei eu-customer=false wird ein Nicht-EU-Vermerk gespeichert; bei wahrheitswertigem eu-customer wird ein Signable-Vertrag zur Unterzeichnung an den angemeldeten Administrator gesendet. Namen und Empfänger stammen aus dem aktuellen Account-/Administratorprofil. Der Aufruf speichert einen Vertragsvorgang und kann externen E-Mail-Versand auslösen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Vertragsvorgangs-ID und insert-Ergebnis; der Versand bestätigt noch keine Unterschrift.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"gdpr_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^gdpr_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^gdpr_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"eu-customer":{"type":["string","number","boolean","null"]}},"additionalProperties":true,"required":["eu-customer"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"GDPR-Vertragsvorgang starten","value":{"data":{"eu-customer":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2305}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list/legal_text Sprachabhängige Account-Rechtstexte abrufen Liest die älteren Account-Rechtstextplätze. Der Filter resource ist erforderlich und wählt general_terms_and_conditions, recall_instruction, privacy_policy oder coupon_designation; language kann die Sprache begrenzen. Diese Texte sind von den versionierten legaltext/legalversion-Dokumenten getrennt. - Operation-ID: get_v2_settings_list_legal_text - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-legal-text-9f682d50ec80/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | ja | resource ist ein erforderlicher Filter. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"resource ist ein erforderlicher Filter.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"resource","value":"privacy_policy"},{"property":"language","value":"de"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list/legal_text' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/legal_text"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/legal_text" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list_legal_text","summary":"Sprachabhängige Account-Rechtstexte abrufen","description":"Liest die älteren Account-Rechtstextplätze. Der Filter resource ist erforderlich und wählt general_terms_and_conditions, recall_instruction, privacy_policy oder coupon_designation; language kann die Sprache begrenzen. Diese Texte sind von den versionierten legaltext/legalversion-Dokumenten getrennt.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"resource ist ein erforderlicher Filter.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"resource","value":"privacy_policy"},{"property":"language","value":"de"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Generische Sammlung der ausgewählten sprachabhängigen Account-Rechtstexte.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"legaltext_012345abcdef_123456":{"id":"legaltext_012345abcdef_123456","value":"Beispiel für einen Datenschutzhinweis.","language":"de","resource":"privacy_policy"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"value":{"type":"string"},"language":{"type":"string"},"resource":{"type":"string","enum":["general_terms_and_conditions","recall_instruction","privacy_policy","coupon_designation"]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"value":{"type":"string"},"language":{"type":"string"},"resource":{"type":"string","enum":["general_terms_and_conditions","recall_instruction","privacy_policy","coupon_designation"]}},"additionalProperties":true}}],"description":"Standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. fields begrenzt die Felder."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings lookup and account configuration reads; no persistence in these branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2445}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/list/legal_text Sprachabhängigen Account-Rechtstext anlegen Legt einen noch fehlenden älteren Rechtstextplatz für resource und language an. value, language und resource sind erforderlich; pro Account, Sprache und Rechtstextplatz darf nur ein Datensatz bestehen. Der Textinhalt wird nach dem nativen Text-Getter normalisiert. - Operation-ID: post_v2_settings_list_legal_text - Dokumentation: /docs/api/v2/operations/post-v2-settings-list-legal-text-4a3f35bf570e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "value": "Beispiel für einen Datenschutzhinweis.", "language": "de", "resource": "privacy_policy" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/list/legal_text' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/legal_text"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "value": "Beispiel für einen Datenschutzhinweis.", "language": "de", "resource": "privacy_policy" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/legal_text" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"value\":\"Beispiel für einen Datenschutzhinweis.\",\"language\":\"de\",\"resource\":\"privacy_policy\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_list_legal_text","summary":"Sprachabhängigen Account-Rechtstext anlegen","description":"Legt einen noch fehlenden älteren Rechtstextplatz für resource und language an. value, language und resource sind erforderlich; pro Account, Sprache und Rechtstextplatz darf nur ein Datensatz bestehen. Der Textinhalt wird nach dem nativen Text-Getter normalisiert.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche Rechtstext-ID und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"value":{},"language":{"type":"string"},"resource":{"type":"string","enum":["general_terms_and_conditions","recall_instruction","privacy_policy","coupon_designation"]}},"additionalProperties":true,"required":["value","language","resource"]},{"type":"array","items":{"type":"object","properties":{"value":{},"language":{"type":"string"},"resource":{"type":"string","enum":["general_terms_and_conditions","recall_instruction","privacy_policy","coupon_designation"]}},"additionalProperties":true,"required":["value","language","resource"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Sprachabhängigen Account-Rechtstext anlegen","value":{"data":{"value":"Beispiel für einen Datenschutzhinweis.","language":"de","resource":"privacy_policy"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2296}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/list/legal_text Sprachabhängigen Account-Rechtstext ändern Aktualisiert value eines älteren Account-Rechtstextplatzes über seine öffentliche Rechtstext-ID. resource und language sind unveränderlich; der Text wird beim Speichern auf die native gespeicherte Länge von 35000 begrenzt. Bestehende native Text-/Accountprüfungen bleiben aktiv. - Operation-ID: put_v2_settings_list_legal_text - Dokumentation: /docs/api/v2/operations/put-v2-settings-list-legal-text-b602cc927fe6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "value": "Aktualisierter beispielhafter Rechtstext." }, "id": "legaltext_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/list/legal_text' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/legal_text"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "value": "Aktualisierter beispielhafter Rechtstext." }, "id": "legaltext_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/legal_text" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"value\":\"Aktualisierter beispielhafter Rechtstext.\"},\"id\":\"legaltext_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_list_legal_text","summary":"Sprachabhängigen Account-Rechtstext ändern","description":"Aktualisiert value eines älteren Account-Rechtstextplatzes über seine öffentliche Rechtstext-ID. resource und language sind unveränderlich; der Text wird beim Speichern auf die native gespeicherte Länge von 35000 begrenzt. Bestehende native Text-/Accountprüfungen bleiben aktiv.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche Rechtstext-ID und update-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"value":{}},"additionalProperties":true},"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Sprachabhängigen Account-Rechtstext ändern","value":{"data":{"value":"Aktualisierter beispielhafter Rechtstext."},"id":"legaltext_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settings.php","line":2358}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/settings/list/legaltext Versionierbaren Rechtstext löschen Markiert die ausgewählte Rechtstextgruppe als gelöscht. Bei /list muss id im Request stehen; bei der Einzelroute ist id die öffentliche legaltext-ID im Pfad. Dieser Aufruf löscht keine allgemeinen Account-Einstellungen. - Operation-ID: delete_v2_settings_list_legaltext - Dokumentation: /docs/api/v2/operations/delete-v2-settings-list-legaltext-9c78a075a5f9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "legaltext_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/settings/list/legaltext' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/legaltext"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "legaltext_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/legaltext" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"legaltext_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_settings_list_legaltext","summary":"Versionierbaren Rechtstext löschen","description":"Markiert die ausgewählte Rechtstextgruppe als gelöscht. Bei /list muss id im Request stehen; bei der Einzelroute ist id die öffentliche legaltext-ID im Pfad. Dieser Aufruf löscht keine allgemeinen Account-Einstellungen.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["settings_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche legaltext-ID und delete-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Versionierbaren Rechtstext löschen","value":{"id":"legaltext_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"deleteSettings","line":2385,"branch":{"file":"application/models/v2/Settings.php","line":2390}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/list/legaltext Versionierbaren Rechtstext anlegen Legt eine Rechtstextgruppe mit sprachabhängigen Anzeigenamen an. name ist das Erkennungsfeld des Einzel-Einfügepfads; seine Schlüssel sind bekannte Sprachcodes und die Werte enthalten name. Der eigentliche Inhalt wird anschließend als legalversion angelegt. - Operation-ID: post_v2_settings_list_legaltext - Dokumentation: /docs/api/v2/operations/post-v2-settings-list-legaltext-02fcd660079d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": { "de": { "name": "Teilnahmebedingungen" }, "en": { "name": "Participation terms" } } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/list/legaltext' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/legaltext"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": { "de": { "name": "Teilnahmebedingungen" }, "en": { "name": "Participation terms" } } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/legaltext" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":{\"de\":{\"name\":\"Teilnahmebedingungen\"},\"en\":{\"name\":\"Participation terms\"}}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_list_legaltext","summary":"Versionierbaren Rechtstext anlegen","description":"Legt eine Rechtstextgruppe mit sprachabhängigen Anzeigenamen an. name ist das Erkennungsfeld des Einzel-Einfügepfads; seine Schlüssel sind bekannte Sprachcodes und die Werte enthalten name. Der eigentliche Inhalt wird anschließend als legalversion angelegt.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche legaltext-ID und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"name":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{}},"additionalProperties":true,"required":["name"]},"description":"Sprachcode -> {name: Anzeigename}; nur bekannte Sprachen werden übernommen."}},"additionalProperties":true,"required":["name"]},{"type":"array","items":{"type":"object","properties":{"name":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{}},"additionalProperties":true,"required":["name"]},"description":"Sprachcode -> {name: Anzeigename}; nur bekannte Sprachen werden übernommen."}},"additionalProperties":true,"required":["name"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Versionierbaren Rechtstext anlegen","value":{"data":{"name":{"de":{"name":"Teilnahmebedingungen"},"en":{"name":"Participation terms"}}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2280}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/list/legalversion Entwurf einer Rechtstextversion anlegen Legt zu legaltext und language einen neuen Entwurf an. Beide Felder sind erforderlich; version wird als nächste Nummer je Rechtstext und Sprache berechnet. is_fixed wird beim Anlegen stets zurückgesetzt, fixed_by_admin/fixed_at werden serverseitig gesetzt. button_text und legal_text enthalten den später anzuzeigenden Inhalt. - Operation-ID: post_v2_settings_list_legalversion - Dokumentation: /docs/api/v2/operations/post-v2-settings-list-legalversion-4baec5d5376a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "legaltext": "legaltext_012345abcdef_123456", "language": "de", "button_text": "Ich akzeptiere", "legal_text": "Beispielhafte Teilnahmebedingungen." } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/list/legalversion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/legalversion"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "legaltext": "legaltext_012345abcdef_123456", "language": "de", "button_text": "Ich akzeptiere", "legal_text": "Beispielhafte Teilnahmebedingungen." } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/legalversion" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"legaltext\":\"legaltext_012345abcdef_123456\",\"language\":\"de\",\"button_text\":\"Ich akzeptiere\",\"legal_text\":\"Beispielhafte Teilnahmebedingungen.\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_list_legalversion","summary":"Entwurf einer Rechtstextversion anlegen","description":"Legt zu legaltext und language einen neuen Entwurf an. Beide Felder sind erforderlich; version wird als nächste Nummer je Rechtstext und Sprache berechnet. is_fixed wird beim Anlegen stets zurückgesetzt, fixed_by_admin/fixed_at werden serverseitig gesetzt. button_text und legal_text enthalten den später anzuzeigenden Inhalt.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche legalversion-ID und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legalversion_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"language":{"type":"string"},"button_text":{},"legal_text":{},"version":{},"is_fixed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"fixed_by_admin":{},"fixed_at":{}},"additionalProperties":true,"required":["legaltext","language"]},{"type":"array","items":{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"language":{"type":"string"},"button_text":{},"legal_text":{},"version":{},"is_fixed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"fixed_by_admin":{},"fixed_at":{}},"additionalProperties":true,"required":["legaltext","language"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Entwurf einer Rechtstextversion anlegen","value":{"data":{"legaltext":"legaltext_012345abcdef_123456","language":"de","button_text":"Ich akzeptiere","legal_text":"Beispielhafte Teilnahmebedingungen."}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2288}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list/mailsystem Mailanbieter und Verbindungsstatus abrufen Liest das aktive Mailsystem, SMTP-/Mailjet-Metadaten, Microsoft-Verbindungsstatus, DNS-Daten und aktuelle Versandhinweise. Passwort und Mailjet-Secret werden ausgelassen. Bei Microsoft können gespeicherte Tokens erneuert werden; für berechtigte Browser-Anmeldungen wird außerdem ein kurzlebiger OAuth-Autorisierungszustand mit Browsercookie für auth_url gespeichert. Daher ist dieser GET kein reiner Leseaufruf. - Operation-ID: get_v2_settings_list_mailsystem - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-mailsystem-f0b490fd6303/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list/mailsystem' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/mailsystem"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/mailsystem" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list_mailsystem","summary":"Mailanbieter und Verbindungsstatus abrufen","description":"Liest das aktive Mailsystem, SMTP-/Mailjet-Metadaten, Microsoft-Verbindungsstatus, DNS-Daten und aktuelle Versandhinweise. Passwort und Mailjet-Secret werden ausgelassen. Bei Microsoft können gespeicherte Tokens erneuert werden; für berechtigte Browser-Anmeldungen wird außerdem ein kurzlebiger OAuth-Autorisierungszustand mit Browsercookie für auth_url gespeichert. Daher ist dieser GET kein reiner Leseaufruf.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Anbieterkonfiguration mit Statusfeldern und optionalen externen DNS-/Account-Daten; sensible Inhalte vertraulich behandeln.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"mailsystem":{"smtp":false,"mailjet":false,"microsoft":false,"edoobox-email":true},"edoobox-email":{"email":"norply@edoobox.email"},"mailjet":{"active":false,"email":"office@example.invalid","key":""},"smtp":{"active":false,"email":"office@example.invalid","user":"","url":"","port":587,"authorize":true,"secure":"tls"},"microsoft":{"active":false,"email":"","connection":false,"has_connection":false,"account":false,"auth_url":""},"mail_system_alerts":[]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"mailsystem":{"type":"object","properties":{"smtp":{"type":"boolean"},"mailjet":{"type":"boolean"},"microsoft":{"type":"boolean"},"edoobox-email":{"type":"boolean"}},"additionalProperties":true},"edoobox-email":{"type":"object","properties":{"email":{"type":"string"}},"additionalProperties":true},"mailjet":{"type":"object","properties":{"active":{"type":"boolean"},"email":{"type":"string"},"key":{"type":"string"},"dns":{}},"additionalProperties":true},"smtp":{"type":"object","properties":{"active":{"type":"boolean"},"email":{"type":"string"},"user":{"type":"string"},"url":{"type":"string"},"port":{"type":"integer"},"authorize":{"type":"boolean"},"secure":{"type":["string","boolean"]}},"additionalProperties":true},"microsoft":{"type":"object","properties":{"active":{"type":"boolean"},"email":{"type":"string"},"connection":{"type":"boolean"},"has_connection":{"type":"boolean"},"account":{},"auth_url":{"type":"string"}},"additionalProperties":true},"mail_system_alerts":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2802,"reason":"Reads tenant SMTP/Mailjet/Microsoft configuration; skips Mailjet secret and SMTP password output in its outbound structures."},"reason":"Read mail settings, automatically refresh existing Microsoft credentials, and for authorized writers issue a new browser-bound Microsoft OAuth handoff.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/16","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/16","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-server-authorization":true,"x-edoobox-server-authorization-source":{"file":"application/libraries/edoobox/integrations/auth/OAuthAuthorizationState.php","line":27,"reason":"The native settings read admits read scope and existing-token maintenance. OAuth state issuance separately requires current admin settings level 3/4 and a non-read-only login; its caught denial leaves auth_url empty. Preserve this exact native branch distinction."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2421}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/list/mailsystem Mailanbieter-Auswahl validieren Dieser Pfad ohne Anbietersegment führt keine Umstellung aus und antwortet mit ED80100/require.mailsystem=false. Für Änderungen muss einer der konkreten Anbieterpfade edoobox-email, mailjet, microsoft oder smtp verwendet werden. - Operation-ID: put_v2_settings_list_mailsystem - Dokumentation: /docs/api/v2/operations/put-v2-settings-list-mailsystem-c1adf6a83ddd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "account_012345abcdef_123456", "data": {} } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/list/mailsystem' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/mailsystem"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "account_012345abcdef_123456", "data": {} }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/mailsystem" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"account_012345abcdef_123456\",\"data\":{}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_list_mailsystem","summary":"Mailanbieter-Auswahl validieren","description":"Dieser Pfad ohne Anbietersegment führt keine Umstellung aus und antwortet mit ED80100/require.mailsystem=false. Für Änderungen muss einer der konkreten Anbieterpfade edoobox-email, mailjet, microsoft oder smtp verwendet werden.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Validierungsfehler ED80100 mit require.mailsystem=false; kein erfolgreicher Dateninhalt.","content":{"application/json":{"example":{"success":true,"error":false,"data":null},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Dieser Fehlerpfad liefert kein data-Feld."},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Mailanbieter-Auswahl validieren","value":{"id":"account_012345abcdef_123456","data":{}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settings.php","line":2334}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/list/mailsystem/edoobox-email edoobox-Mailversand aktivieren Schaltet bei wahrheitswertigem active den Standardversand über edoobox ein und deaktiviert andere Mailanbieter. Ohne aktives active erfolgt keine Umstellung; aktuelle Versandvorfälle werden beim Einschalten aufgelöst. Bei /list verlangt der generische PUT-Normalisierer zusätzlich id im Request; verwendet wird der aktuelle Account. - Operation-ID: put_v2_settings_list_mailsystem_edoobox_email - Dokumentation: /docs/api/v2/operations/put-v2-settings-list-mailsystem-edoobox-email-fa659362c6a8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "active": true }, "id": "account_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/list/mailsystem/edoobox-email' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/mailsystem/edoobox-email"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "active": true }, "id": "account_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/mailsystem/edoobox-email" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"active\":true},\"id\":\"account_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_list_mailsystem_edoobox_email","summary":"edoobox-Mailversand aktivieren","description":"Schaltet bei wahrheitswertigem active den Standardversand über edoobox ein und deaktiviert andere Mailanbieter. Ohne aktives active erfolgt keine Umstellung; aktuelle Versandvorfälle werden beim Einschalten aufgelöst. Bei /list verlangt der generische PUT-Normalisierer zusätzlich id im Request; verwendet wird der aktuelle Account.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Account-ID und update; je Anbieter zusätzlich connect_error, Restversuche, Prüfdaten oder disconnected.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"},"connect_error":{"type":"boolean"},"too_many_tries":{"type":"integer"},"connect_error_code":{"type":"object","properties":{},"additionalProperties":true},"disconnected":{"type":"boolean"},"verification":{"type":"string"},"mailjet":{"type":"object","properties":{"dns":{},"webhook":{}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"active":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"}},"additionalProperties":true},"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"edoobox-Mailversand aktivieren","value":{"data":{"active":true},"id":"account_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settingsfunc.php","line":606}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/list/mailsystem/mailjet Mailjet konfigurieren und prüfen Prüft Mailjet-Zugangsdaten, aktiviert bei erfolgreicher Verbindung Mailjet, speichert Schlüssel und Absenderadresse und ergänzt fehlende Webhooks. Bestehende Anbieter werden deaktiviert; DNS- und Webhookdaten können zurückgegeben werden. Accountweite Versandsperre und begrenzte Verbindungsversuche bleiben maßgeblich. Bei /list verlangt der generische PUT-Normalisierer zusätzlich id im Request; verwendet wird der aktuelle Account. - Operation-ID: put_v2_settings_list_mailsystem_mailjet - Dokumentation: /docs/api/v2/operations/put-v2-settings-list-mailsystem-mailjet-57ddc9657487/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "active": true, "email": "office@example.invalid", "key": "synthetic-mailjet-key", "secret": "synthetic-mailjet-secret" }, "id": "account_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/list/mailsystem/mailjet' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/mailsystem/mailjet"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "active": true, "email": "office@example.invalid", "key": "synthetic-mailjet-key", "secret": "synthetic-mailjet-secret" }, "id": "account_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/mailsystem/mailjet" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"active\":true,\"email\":\"office@example.invalid\",\"key\":\"synthetic-mailjet-key\",\"secret\":\"synthetic-mailjet-secret\"},\"id\":\"account_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_list_mailsystem_mailjet","summary":"Mailjet konfigurieren und prüfen","description":"Prüft Mailjet-Zugangsdaten, aktiviert bei erfolgreicher Verbindung Mailjet, speichert Schlüssel und Absenderadresse und ergänzt fehlende Webhooks. Bestehende Anbieter werden deaktiviert; DNS- und Webhookdaten können zurückgegeben werden. Accountweite Versandsperre und begrenzte Verbindungsversuche bleiben maßgeblich. Bei /list verlangt der generische PUT-Normalisierer zusätzlich id im Request; verwendet wird der aktuelle Account.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Account-ID und update; je Anbieter zusätzlich connect_error, Restversuche, Prüfdaten oder disconnected.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"connect_error":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"},"connect_error":{"type":"boolean"},"too_many_tries":{"type":"integer"},"connect_error_code":{"type":"object","properties":{},"additionalProperties":true},"disconnected":{"type":"boolean"},"verification":{"type":"string"},"mailjet":{"type":"object","properties":{"dns":{},"webhook":{}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"active":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"email":{"type":"string"},"key":{"type":"string","writeOnly":true},"secret":{"type":"string","writeOnly":true}},"additionalProperties":true,"required":["active","email","key","secret"]},"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Mailjet konfigurieren und prüfen","value":{"data":{"active":true,"email":"office@example.invalid","key":"synthetic-mailjet-key","secret":"synthetic-mailjet-secret"},"id":"account_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settingsfunc.php","line":609}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/list/mailsystem/microsoft Microsoft-Mailversand konfigurieren oder trennen Mit disconnect=true wird die Microsoft-Verbindung ausdrücklich getrennt. Sonst sind email und ein echter boolescher active erforderlich; es muss bereits eine OAuth-Verbindung bestehen. Die Prüfung sendet eine Testmail, speichert bei Erfolg Absender/Aktivierung und deaktiviert bei Aktivierung andere Anbieter. Ein Fehler liefert connect_error statt einer erfolgreichen Umstellung. Bei /list verlangt der generische PUT-Normalisierer zusätzlich id im Request; verwendet wird der aktuelle Account. - Operation-ID: put_v2_settings_list_mailsystem_microsoft - Dokumentation: /docs/api/v2/operations/put-v2-settings-list-mailsystem-microsoft-5d683c5c1deb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "email": "office@example.invalid", "active": true }, "id": "account_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/list/mailsystem/microsoft' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/mailsystem/microsoft"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "email": "office@example.invalid", "active": true }, "id": "account_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/mailsystem/microsoft" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"email\":\"office@example.invalid\",\"active\":true},\"id\":\"account_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_list_mailsystem_microsoft","summary":"Microsoft-Mailversand konfigurieren oder trennen","description":"Mit disconnect=true wird die Microsoft-Verbindung ausdrücklich getrennt. Sonst sind email und ein echter boolescher active erforderlich; es muss bereits eine OAuth-Verbindung bestehen. Die Prüfung sendet eine Testmail, speichert bei Erfolg Absender/Aktivierung und deaktiviert bei Aktivierung andere Anbieter. Ein Fehler liefert connect_error statt einer erfolgreichen Umstellung. Bei /list verlangt der generische PUT-Normalisierer zusätzlich id im Request; verwendet wird der aktuelle Account.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Account-ID und update; je Anbieter zusätzlich connect_error, Restversuche, Prüfdaten oder disconnected.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"connect_error":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"},"connect_error":{"type":"boolean"},"too_many_tries":{"type":"integer"},"connect_error_code":{"type":"object","properties":{},"additionalProperties":true},"disconnected":{"type":"boolean"},"verification":{"type":"string"},"mailjet":{"type":"object","properties":{"dns":{},"webhook":{}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"disconnect":{"const":true}},"additionalProperties":true,"required":["disconnect"]},{"type":"object","properties":{"disconnect":{"const":false},"email":{"type":"string","format":"email"},"active":{"type":"boolean"}},"additionalProperties":true,"required":["email","active"]}]},"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Microsoft-Mailversand konfigurieren oder trennen","value":{"data":{"email":"office@example.invalid","active":true},"id":"account_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settingsfunc.php","line":612}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/settings/list/mailsystem/smtp SMTP-Versand konfigurieren und prüfen Prüft SMTP-Verbindungsdaten durch Versand einer Testmail und speichert bei Erfolg die Konfiguration als aktives Mailsystem. Ein vorhandenes Passwort kann wiederverwendet werden. Accountweite Versandsperre und begrenzte Verbindungsversuche bleiben maßgeblich. active/authorize werden nativ als Wahrheitswerte normalisiert; port wird zur Ganzzahl konvertiert. Bei /list verlangt der generische PUT-Normalisierer zusätzlich id im Request; verwendet wird der aktuelle Account. - Operation-ID: put_v2_settings_list_mailsystem_smtp - Dokumentation: /docs/api/v2/operations/put-v2-settings-list-mailsystem-smtp-ec8a6da70823/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "active": true, "email": "office@example.invalid", "user": "office@example.invalid", "password": "synthetic-smtp-password", "url": "smtp.example.invalid", "port": 587, "authorize": true, "secure": "tls" }, "id": "account_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/settings/list/mailsystem/smtp' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/mailsystem/smtp"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "active": true, "email": "office@example.invalid", "user": "office@example.invalid", "password": "synthetic-smtp-password", "url": "smtp.example.invalid", "port": 587, "authorize": true, "secure": "tls" }, "id": "account_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/mailsystem/smtp" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"active\":true,\"email\":\"office@example.invalid\",\"user\":\"office@example.invalid\",\"password\":\"synthetic-smtp-password\",\"url\":\"smtp.example.invalid\",\"port\":587,\"authorize\":true,\"secure\":\"tls\"},\"id\":\"account_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_settings_list_mailsystem_smtp","summary":"SMTP-Versand konfigurieren und prüfen","description":"Prüft SMTP-Verbindungsdaten durch Versand einer Testmail und speichert bei Erfolg die Konfiguration als aktives Mailsystem. Ein vorhandenes Passwort kann wiederverwendet werden. Accountweite Versandsperre und begrenzte Verbindungsversuche bleiben maßgeblich. active/authorize werden nativ als Wahrheitswerte normalisiert; port wird zur Ganzzahl konvertiert. Bei /list verlangt der generische PUT-Normalisierer zusätzlich id im Request; verwendet wird der aktuelle Account.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Account-ID und update; je Anbieter zusätzlich connect_error, Restversuche, Prüfdaten oder disconnected.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"account_012345abcdef_123456","update":true,"connect_error":false}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"update":{"type":"boolean"},"connect_error":{"type":"boolean"},"too_many_tries":{"type":"integer"},"connect_error_code":{"type":"object","properties":{},"additionalProperties":true},"disconnected":{"type":"boolean"},"verification":{"type":"string"},"mailjet":{"type":"object","properties":{"dns":{},"webhook":{}},"additionalProperties":true}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"active":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"email":{"type":"string","format":"email"},"user":{},"password":{"type":"string","writeOnly":true,"description":"Kann entfallen, wenn ein gespeichertes Passwort wiederverwendbar ist."},"url":{},"port":{"type":["number","string"],"description":"Wird zur Ganzzahl konvertiert und muss positiv sein."},"authorize":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"secure":{"type":["string","boolean","null"],"description":"ssl, tls oder ein leerer/false-Wert für unverschlüsselt; andere Werte normalisiert der Legacy-Getter."}},"additionalProperties":true,"required":["active","email","user","url","port","authorize","secure"]},"id":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"SMTP-Versand konfigurieren und prüfen","value":{"data":{"active":true,"email":"office@example.invalid","user":"office@example.invalid","password":"synthetic-smtp-password","url":"smtp.example.invalid","port":587,"authorize":true,"secure":"tls"},"id":"account_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"updateSettings","line":2324,"branch":{"file":"application/models/v2/Settingsfunc.php","line":615}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list/paper_sizes Unterstützte PDF-Papiergrößen abrufen Liest die benannten Papiergrößen des PDF-Adapters. Jeder Schlüssel enthält Breite und Höhe in PDF-Punkten; die Werte können für pdf_paper_size verwendet werden. - Operation-ID: get_v2_settings_list_paper_sizes - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-paper-sizes-3e788b84e97a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list/paper_sizes' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/paper_sizes"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/paper_sizes" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list_paper_sizes","summary":"Unterstützte PDF-Papiergrößen abrufen","description":"Liest die benannten Papiergrößen des PDF-Adapters. Jeder Schlüssel enthält Breite und Höhe in PDF-Punkten; die Werte können für pdf_paper_size verwendet werden.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Objekt mit Papierformatnamen und [Breite,Höhe] in PDF-Punkten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"a4":[595.28,841.89],"letter":[612,792]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","additionalProperties":{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":2}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings lookup and account configuration reads; no persistence in these branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2479}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/settings/list/timezones Verfügbare Zeitzonen abrufen Listet die vom System hinterlegten Länderkennungen und Zeitzonennamen zur Auswahl von format_timezone. Der Aufruf verwendet keine generische Suche oder Paginierung. - Operation-ID: get_v2_settings_list_timezones - Dokumentation: /docs/api/v2/operations/get-v2-settings-list-timezones-70d4560fb736/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/settings/list/timezones' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/list/timezones"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/list/timezones" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_settings_list_timezones","summary":"Verfügbare Zeitzonen abrufen","description":"Listet die vom System hinterlegten Länderkennungen und Zeitzonennamen zur Auswahl von format_timezone. Der Aufruf verwendet keine generische Suche oder Paginierung.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Liste aus country_code und timezone.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"country_code":"CH","timezone":"Europe/Zurich"}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"country_code":{"type":"string"},"timezone":{"type":"string"}},"additionalProperties":true,"required":["country_code","timezone"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Settings.php","line":2586},"reason":"Settings lookup and account configuration reads; no persistence in these branches."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"getSettings","line":2408,"branch":{"file":"application/models/v2/Settings.php","line":2459}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/text/legaltext Versionierbaren Rechtstext anlegen Legt eine Rechtstextgruppe mit sprachabhängigen Anzeigenamen an. name ist das Erkennungsfeld des Einzel-Einfügepfads; seine Schlüssel sind bekannte Sprachcodes und die Werte enthalten name. Der eigentliche Inhalt wird anschließend als legalversion angelegt. - Operation-ID: post_v2_settings_text_legaltext - Dokumentation: /docs/api/v2/operations/post-v2-settings-text-legaltext-9d374d33f791/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": { "de": { "name": "Teilnahmebedingungen" }, "en": { "name": "Participation terms" } } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/text/legaltext' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/text/legaltext"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": { "de": { "name": "Teilnahmebedingungen" }, "en": { "name": "Participation terms" } } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/text/legaltext" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":{\"de\":{\"name\":\"Teilnahmebedingungen\"},\"en\":{\"name\":\"Participation terms\"}}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_text_legaltext","summary":"Versionierbaren Rechtstext anlegen","description":"Legt eine Rechtstextgruppe mit sprachabhängigen Anzeigenamen an. name ist das Erkennungsfeld des Einzel-Einfügepfads; seine Schlüssel sind bekannte Sprachcodes und die Werte enthalten name. Der eigentliche Inhalt wird anschließend als legalversion angelegt.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche legaltext-ID und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legaltext_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"name":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{}},"additionalProperties":true,"required":["name"]},"description":"Sprachcode -> {name: Anzeigename}; nur bekannte Sprachen werden übernommen."}},"additionalProperties":true,"required":["name"]},{"type":"array","items":{"type":"object","properties":{"name":{"type":"object","additionalProperties":{"type":"object","properties":{"name":{}},"additionalProperties":true,"required":["name"]},"description":"Sprachcode -> {name: Anzeigename}; nur bekannte Sprachen werden übernommen."}},"additionalProperties":true,"required":["name"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Versionierbaren Rechtstext anlegen","value":{"data":{"name":{"de":{"name":"Teilnahmebedingungen"},"en":{"name":"Participation terms"}}}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settings.php","line":2268,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2280}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/settings/text/legalversion Entwurf einer Rechtstextversion anlegen Legt zu legaltext und language einen neuen Entwurf an. Beide Felder sind erforderlich; version wird als nächste Nummer je Rechtstext und Sprache berechnet. is_fixed wird beim Anlegen stets zurückgesetzt, fixed_by_admin/fixed_at werden serverseitig gesetzt. button_text und legal_text enthalten den später anzuzeigenden Inhalt. - Operation-ID: post_v2_settings_text_legalversion - Dokumentation: /docs/api/v2/operations/post-v2-settings-text-legalversion-bcc2d82b1026/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "legaltext": "legaltext_012345abcdef_123456", "language": "de", "button_text": "Ich akzeptiere", "legal_text": "Beispielhafte Teilnahmebedingungen." } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/settings/text/legalversion' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/settings/text/legalversion"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "legaltext": "legaltext_012345abcdef_123456", "language": "de", "button_text": "Ich akzeptiere", "legal_text": "Beispielhafte Teilnahmebedingungen." } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/settings/text/legalversion" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"legaltext\":\"legaltext_012345abcdef_123456\",\"language\":\"de\",\"button_text\":\"Ich akzeptiere\",\"legal_text\":\"Beispielhafte Teilnahmebedingungen.\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_settings_text_legalversion","summary":"Entwurf einer Rechtstextversion anlegen","description":"Legt zu legaltext und language einen neuen Entwurf an. Beide Felder sind erforderlich; version wird als nächste Nummer je Rechtstext und Sprache berechnet. is_fixed wird beim Anlegen stets zurückgesetzt, fixed_by_admin/fixed_at werden serverseitig gesetzt. button_text und legal_text enthalten den später anzuzeigenden Inhalt.","tags":["settings"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Neue öffentliche legalversion-ID und insert-Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"legalversion_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^legalversion_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."}}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"language":{"type":"string"},"button_text":{},"legal_text":{},"version":{},"is_fixed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"fixed_by_admin":{},"fixed_at":{}},"additionalProperties":true,"required":["legaltext","language"]},{"type":"array","items":{"type":"object","properties":{"legaltext":{"type":"string","pattern":"^legaltext_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Account; keine interne Datenbank-ID."},"language":{"type":"string"},"button_text":{},"legal_text":{},"version":{},"is_fixed":{"type":["string","number","boolean","null","array","object"],"x-edoobox-canonicalType":"boolean"},"fixed_by_admin":{},"fixed_at":{}},"additionalProperties":true,"required":["legaltext","language"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Entwurf einer Rechtstextversion anlegen","value":{"data":{"legaltext":"legaltext_012345abcdef_123456","language":"de","button_text":"Ich akzeptiere","legal_text":"Beispielhafte Teilnahmebedingungen."}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Settings.php","line":2276,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/06-admin-settings.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Settings.php","method":"postSettings","line":2261,"branch":{"file":"application/models/v2/Settings.php","line":2288}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"settings","short":"setti","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/support/error-feedback-reports Fehler- oder Ideenmeldung mit Anhängen speichern Speichert einen Supportbericht mit optionalen Screenshots und Replay-Ereignissen. data muss ein nicht leeres Objekt sein; Metadaten sind optional und werden normalisiert. Bilder werden aus PNG-/JPEG-/WebP-dataUrls gelesen (höchstens 8 MiB je Bild); Replay-JSON darf höchstens 50 MiB umfassen. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. Berichte mit route.name=pageRegister oder /pages/register werden vom Controller deaktiviert; dann antwortet data.disabled=true mit reason=signup_error_feedback_disabled. - Operation-ID: post_v2_support_error_feedback_reports - Dokumentation: /docs/api/v2/operations/post-v2-support-error-feedback-reports-473f173f35ee/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "payloadVersion": 1, "trigger": "manual", "feedbackType": "bug", "comment": "Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.", "route": { "name": "offers", "path": "/offers", "fullPath": "/offers" }, "appContext": { "appVersion": "example" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/support/error-feedback-reports' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/support/error-feedback-reports"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "payloadVersion": 1, "trigger": "manual", "feedbackType": "bug", "comment": "Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.", "route": { "name": "offers", "path": "/offers", "fullPath": "/offers" }, "appContext": { "appVersion": "example" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/support/error-feedback-reports" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"payloadVersion\":1,\"trigger\":\"manual\",\"feedbackType\":\"bug\",\"comment\":\"Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.\",\"route\":{\"name\":\"offers\",\"path\":\"/offers\",\"fullPath\":\"/offers\"},\"appContext\":{\"appVersion\":\"example\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_support_error_feedback_reports","summary":"Fehler- oder Ideenmeldung mit Anhängen speichern","description":"Speichert einen Supportbericht mit optionalen Screenshots und Replay-Ereignissen. data muss ein nicht leeres Objekt sein; Metadaten sind optional und werden normalisiert. Bilder werden aus PNG-/JPEG-/WebP-dataUrls gelesen (höchstens 8 MiB je Bild); Replay-JSON darf höchstens 50 MiB umfassen. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. Berichte mit route.name=pageRegister oder /pages/register werden vom Controller deaktiviert; dann antwortet data.disabled=true mit reason=signup_error_feedback_disabled.","tags":["support"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Berichts-/Anhangstatus stehen auf oberster Ebene, nicht unter data. Tabellen-/Speicherfehler können ebenfalls HTTP 200 verwenden. artifacts.status muss für jeden angeforderten Anhang geprüft werden.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"const":true},"error":{"const":false},"status":{"const":"stored"},"reportId":{"type":"string","description":"Öffentlicher Berichtsschlüssel efb_…"},"receivedAt":{"type":"string","format":"date-time"},"artifacts":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"byteSize":{"type":"integer"}},"additionalProperties":true,"required":["key","type","status","byteSize"]}}},"additionalProperties":true,"required":["success","error"]},{"type":"object","properties":{"success":{"const":false},"error":{"type":"string"},"error_code":{"type":"string"}},"additionalProperties":true,"required":["error"]}]},"example":{"success":true,"error":false,"status":"stored","reportId":"efb_syntheticexample","receivedAt":"2027-04-01T10:00:00+00:00","artifacts":[]}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"payloadVersion":{"type":["integer","string"],"description":"Auf 1–99 normalisiert; Standard 1 bei create, 2 bei prepare."},"trigger":{"type":"string","description":"manual erzeugt unread, sonst automatic."},"feedbackType":{"description":"idea bzw. idee normalisiert zu idea; alle anderen Werte werden bug.","examples":["bug"]},"feedback_type":{"description":"Alias für feedbackType."},"comment":{"description":"Kommentar; auf 6000 Zeichen normalisiert.","x-edoobox-canonicalType":"string"},"appContext":{"type":"object","properties":{"accountId":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["account_012345abcdef_123456"]},"adminId":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]},"APP_VERSION":{"type":"string"},"appVersion":{"type":"string"},"environment":{"type":"string"},"userAgent":{"type":"string"}},"additionalProperties":true},"route":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"fullPath":{"type":"string"}},"additionalProperties":true},"telemetry":{"type":"object","properties":{"viewport":{}},"additionalProperties":true},"annotations":{},"visualScreenshot":{"type":"object","properties":{"mimeType":{"type":"string","description":"Bei create aus dataUrl abgeleitet; bei prepare image/png, image/jpeg oder image/webp."},"width":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"height":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"capturedAt":{"type":"string"},"source":{"type":"string"},"redactedByAnnotatedScreenshot":{"description":"Wird per PHP-Wahrheitsvergleich normalisiert.","examples":[true]},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string","description":"Optionaler SHA-256-Inhaltshash (64 Hexzeichen)."},"dataUrl":{"type":"string","description":"data:image/(png|jpeg|webp);base64,..."}},"additionalProperties":true},"annotatedScreenshot":{"type":"object","properties":{"mimeType":{"type":"string","description":"Bei create aus dataUrl abgeleitet; bei prepare image/png, image/jpeg oder image/webp."},"width":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"height":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"capturedAt":{"type":"string"},"source":{"type":"string"},"redactedByAnnotatedScreenshot":{"description":"Wird per PHP-Wahrheitsvergleich normalisiert.","examples":[true]},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string","description":"Optionaler SHA-256-Inhaltshash (64 Hexzeichen)."},"dataUrl":{"type":"string","description":"data:image/(png|jpeg|webp);base64,..."}},"additionalProperties":true},"replay":{"type":"object","properties":{"capturedAt":{"type":"string"},"meta":{},"eventCount":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string"},"events":{"type":"array","items":{}}},"additionalProperties":true}},"additionalProperties":true,"minProperties":1}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Fehler- oder Ideenmeldung mit Anhängen speichern","value":{"data":{"payloadVersion":1,"trigger":"manual","feedbackType":"bug","comment":"Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.","route":{"name":"offers","path":"/offers","fullPath":"/offers"},"appContext":{"appVersion":"example"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Support.php","method":"postSupport","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"support","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/support/error-feedback-reports-placeholder Fehler- oder Ideenmeldung mit Anhängen speichern Speichert einen Supportbericht mit optionalen Screenshots und Replay-Ereignissen. data muss ein nicht leeres Objekt sein; Metadaten sind optional und werden normalisiert. Bilder werden aus PNG-/JPEG-/WebP-dataUrls gelesen (höchstens 8 MiB je Bild); Replay-JSON darf höchstens 50 MiB umfassen. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. - Operation-ID: post_v2_support_error_feedback_reports_placeholder - Dokumentation: /docs/api/v2/operations/post-v2-support-error-feedback-reports-placeholder-e6f12c60c3f6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "payloadVersion": 1, "trigger": "manual", "feedbackType": "bug", "comment": "Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.", "route": { "name": "offers", "path": "/offers", "fullPath": "/offers" }, "appContext": { "appVersion": "example" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/support/error-feedback-reports-placeholder' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/support/error-feedback-reports-placeholder"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "payloadVersion": 1, "trigger": "manual", "feedbackType": "bug", "comment": "Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.", "route": { "name": "offers", "path": "/offers", "fullPath": "/offers" }, "appContext": { "appVersion": "example" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/support/error-feedback-reports-placeholder" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"payloadVersion\":1,\"trigger\":\"manual\",\"feedbackType\":\"bug\",\"comment\":\"Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.\",\"route\":{\"name\":\"offers\",\"path\":\"/offers\",\"fullPath\":\"/offers\"},\"appContext\":{\"appVersion\":\"example\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_support_error_feedback_reports_placeholder","summary":"Fehler- oder Ideenmeldung mit Anhängen speichern","description":"Speichert einen Supportbericht mit optionalen Screenshots und Replay-Ereignissen. data muss ein nicht leeres Objekt sein; Metadaten sind optional und werden normalisiert. Bilder werden aus PNG-/JPEG-/WebP-dataUrls gelesen (höchstens 8 MiB je Bild); Replay-JSON darf höchstens 50 MiB umfassen. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden.","tags":["support"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Berichts-/Anhangstatus stehen auf oberster Ebene, nicht unter data. Tabellen-/Speicherfehler können ebenfalls HTTP 200 verwenden. artifacts.status muss für jeden angeforderten Anhang geprüft werden.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"const":true},"error":{"const":false},"status":{"const":"stored"},"reportId":{"type":"string","description":"Öffentlicher Berichtsschlüssel efb_…"},"receivedAt":{"type":"string","format":"date-time"},"artifacts":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"byteSize":{"type":"integer"}},"additionalProperties":true,"required":["key","type","status","byteSize"]}}},"additionalProperties":true,"required":["success","error"]},{"type":"object","properties":{"success":{"const":false},"error":{"type":"string"},"error_code":{"type":"string"}},"additionalProperties":true,"required":["error"]}]},"example":{"success":true,"error":false,"status":"stored","reportId":"efb_syntheticexample","receivedAt":"2027-04-01T10:00:00+00:00","artifacts":[]}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"payloadVersion":{"type":["integer","string"],"description":"Auf 1–99 normalisiert; Standard 1 bei create, 2 bei prepare."},"trigger":{"type":"string","description":"manual erzeugt unread, sonst automatic."},"feedbackType":{"description":"idea bzw. idee normalisiert zu idea; alle anderen Werte werden bug.","examples":["bug"]},"feedback_type":{"description":"Alias für feedbackType."},"comment":{"description":"Kommentar; auf 6000 Zeichen normalisiert.","x-edoobox-canonicalType":"string"},"appContext":{"type":"object","properties":{"accountId":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["account_012345abcdef_123456"]},"adminId":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]},"APP_VERSION":{"type":"string"},"appVersion":{"type":"string"},"environment":{"type":"string"},"userAgent":{"type":"string"}},"additionalProperties":true},"route":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"fullPath":{"type":"string"}},"additionalProperties":true},"telemetry":{"type":"object","properties":{"viewport":{}},"additionalProperties":true},"annotations":{},"visualScreenshot":{"type":"object","properties":{"mimeType":{"type":"string","description":"Bei create aus dataUrl abgeleitet; bei prepare image/png, image/jpeg oder image/webp."},"width":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"height":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"capturedAt":{"type":"string"},"source":{"type":"string"},"redactedByAnnotatedScreenshot":{"description":"Wird per PHP-Wahrheitsvergleich normalisiert.","examples":[true]},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string","description":"Optionaler SHA-256-Inhaltshash (64 Hexzeichen)."},"dataUrl":{"type":"string","description":"data:image/(png|jpeg|webp);base64,..."}},"additionalProperties":true},"annotatedScreenshot":{"type":"object","properties":{"mimeType":{"type":"string","description":"Bei create aus dataUrl abgeleitet; bei prepare image/png, image/jpeg oder image/webp."},"width":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"height":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"capturedAt":{"type":"string"},"source":{"type":"string"},"redactedByAnnotatedScreenshot":{"description":"Wird per PHP-Wahrheitsvergleich normalisiert.","examples":[true]},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string","description":"Optionaler SHA-256-Inhaltshash (64 Hexzeichen)."},"dataUrl":{"type":"string","description":"data:image/(png|jpeg|webp);base64,..."}},"additionalProperties":true},"replay":{"type":"object","properties":{"capturedAt":{"type":"string"},"meta":{},"eventCount":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string"},"events":{"type":"array","items":{}}},"additionalProperties":true}},"additionalProperties":true,"minProperties":1}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Fehler- oder Ideenmeldung mit Anhängen speichern","value":{"data":{"payloadVersion":1,"trigger":"manual","feedbackType":"bug","comment":"Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.","route":{"name":"offers","path":"/offers","fullPath":"/offers"},"appContext":{"appVersion":"example"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Support.php","method":"postSupport","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"support","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/support/error-feedback-reports-placeholder/finalize Meldungsanhänge prüfen und abschließen Prüft vorbereitete Anhänge eines eigenen Berichts auf Existenz, MIME, Größe und optionalen Hash und aktualisiert die Speicherstatus. reportId oder Alias feedbackKey ist erforderlich. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. - Operation-ID: post_v2_support_error_feedback_reports_placeholder_finalize - Dokumentation: /docs/api/v2/operations/post-v2-support-error-feedback-reports-placeholder-finalize-3bfef81dde96/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "reportId": "efb_syntheticexample" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/support/error-feedback-reports-placeholder/finalize' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/support/error-feedback-reports-placeholder/finalize"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "reportId": "efb_syntheticexample" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/support/error-feedback-reports-placeholder/finalize" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"reportId\":\"efb_syntheticexample\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_support_error_feedback_reports_placeholder_finalize","summary":"Meldungsanhänge prüfen und abschließen","description":"Prüft vorbereitete Anhänge eines eigenen Berichts auf Existenz, MIME, Größe und optionalen Hash und aktualisiert die Speicherstatus. reportId oder Alias feedbackKey ist erforderlich. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden.","tags":["support"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Berichts-/Anhangstatus stehen auf oberster Ebene, nicht unter data. Tabellen-/Speicherfehler können ebenfalls HTTP 200 verwenden. artifacts.status muss für jeden angeforderten Anhang geprüft werden.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"const":true},"error":{"const":false},"status":{"const":"stored"},"reportId":{"type":"string"},"receivedAt":{"type":"string","format":"date-time"},"artifacts":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"byteSize":{"type":"integer"}},"additionalProperties":true,"required":["key","type","status","byteSize"]}}},"additionalProperties":true,"required":["success","error"]},{"type":"object","properties":{"success":{"const":false},"error":{"type":"string"},"error_code":{"type":"string"}},"additionalProperties":true,"required":["error"]}]},"example":{"success":true,"error":false,"status":"stored","reportId":"efb_syntheticexample","receivedAt":"2027-04-01T10:00:00+00:00","artifacts":[]}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"reportId":{"type":"string","description":"Berichtsschlüssel aus prepare/create."},"feedbackKey":{"type":"string","description":"Alias für reportId."}},"additionalProperties":true,"anyOf":[{"required":["reportId"]},{"required":["feedbackKey"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Meldungsanhänge prüfen und abschließen","value":{"data":{"reportId":"efb_syntheticexample"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Support.php","method":"postSupport","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"support","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/support/error-feedback-reports-placeholder/prepare Supportbericht und signierte Anhang-Uploads vorbereiten Speichert Bericht und Anhang-Metadaten und erzeugt signierte PUT-URLs für 30 Minuten. Bilder benötigen positive byteSize bis 8 MiB und MIME image/png, image/jpeg oder image/webp; Replay benötigt positive byteSize bis 50 MiB. sha256 ist optional. Nach dem Byte-Upload finalize aufrufen; bei Screenshot-CORS-Problemen steht upload zur Verfügung. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. - Operation-ID: post_v2_support_error_feedback_reports_placeholder_prepare - Dokumentation: /docs/api/v2/operations/post-v2-support-error-feedback-reports-placeholder-prepare-f227607603c6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "payloadVersion": 2, "trigger": "manual", "feedbackType": "bug", "comment": "Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.", "route": { "name": "offers", "path": "/offers", "fullPath": "/offers" }, "appContext": { "appVersion": "example" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/support/error-feedback-reports-placeholder/prepare' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/support/error-feedback-reports-placeholder/prepare"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "payloadVersion": 2, "trigger": "manual", "feedbackType": "bug", "comment": "Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.", "route": { "name": "offers", "path": "/offers", "fullPath": "/offers" }, "appContext": { "appVersion": "example" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/support/error-feedback-reports-placeholder/prepare" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"payloadVersion\":2,\"trigger\":\"manual\",\"feedbackType\":\"bug\",\"comment\":\"Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.\",\"route\":{\"name\":\"offers\",\"path\":\"/offers\",\"fullPath\":\"/offers\"},\"appContext\":{\"appVersion\":\"example\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_support_error_feedback_reports_placeholder_prepare","summary":"Supportbericht und signierte Anhang-Uploads vorbereiten","description":"Speichert Bericht und Anhang-Metadaten und erzeugt signierte PUT-URLs für 30 Minuten. Bilder benötigen positive byteSize bis 8 MiB und MIME image/png, image/jpeg oder image/webp; Replay benötigt positive byteSize bis 50 MiB. sha256 ist optional. Nach dem Byte-Upload finalize aufrufen; bei Screenshot-CORS-Problemen steht upload zur Verfügung. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden.","tags":["support"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Berichts-/Anhangstatus stehen auf oberster Ebene, nicht unter data. Tabellen-/Speicherfehler können ebenfalls HTTP 200 verwenden. artifacts.status muss für jeden angeforderten Anhang geprüft werden.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"const":true},"error":{"const":false},"status":{"const":"prepared"},"reportId":{"type":"string","description":"Öffentlicher Berichtsschlüssel efb_…"},"receivedAt":{"type":"string","format":"date-time"},"artifacts":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"byteSize":{"type":"integer"},"mimeType":{"type":"string"},"uploadUrl":{"type":"string","description":"Kurzlebige signierte PUT-URL; vertraulich behandeln."}},"additionalProperties":true}},"uploadExpiresAt":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["success","error"]},{"type":"object","properties":{"success":{"const":false},"error":{"type":"string"},"error_code":{"type":"string"}},"additionalProperties":true,"required":["error"]}]},"example":{"success":true,"error":false,"status":"prepared","reportId":"efb_syntheticexample","receivedAt":"2027-04-01T10:00:00+00:00","artifacts":[],"uploadExpiresAt":"2027-04-01T10:30:00+00:00"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"payloadVersion":{"type":["integer","string"],"description":"Auf 1–99 normalisiert; Standard 1 bei create, 2 bei prepare."},"trigger":{"type":"string","description":"manual erzeugt unread, sonst automatic."},"feedbackType":{"description":"idea bzw. idee normalisiert zu idea; alle anderen Werte werden bug.","examples":["bug"]},"feedback_type":{"description":"Alias für feedbackType."},"comment":{"description":"Kommentar; auf 6000 Zeichen normalisiert.","x-edoobox-canonicalType":"string"},"appContext":{"type":"object","properties":{"accountId":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["account_012345abcdef_123456"]},"adminId":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]},"APP_VERSION":{"type":"string"},"appVersion":{"type":"string"},"environment":{"type":"string"},"userAgent":{"type":"string"}},"additionalProperties":true},"route":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"fullPath":{"type":"string"}},"additionalProperties":true},"telemetry":{"type":"object","properties":{"viewport":{}},"additionalProperties":true},"annotations":{},"visualScreenshot":{"type":"object","properties":{"mimeType":{"type":"string","description":"Bei create aus dataUrl abgeleitet; bei prepare image/png, image/jpeg oder image/webp."},"width":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"height":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"capturedAt":{"type":"string"},"source":{"type":"string"},"redactedByAnnotatedScreenshot":{"description":"Wird per PHP-Wahrheitsvergleich normalisiert.","examples":[true]},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string","description":"Optionaler SHA-256-Inhaltshash (64 Hexzeichen)."}},"additionalProperties":true},"annotatedScreenshot":{"type":"object","properties":{"mimeType":{"type":"string","description":"Bei create aus dataUrl abgeleitet; bei prepare image/png, image/jpeg oder image/webp."},"width":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"height":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"capturedAt":{"type":"string"},"source":{"type":"string"},"redactedByAnnotatedScreenshot":{"description":"Wird per PHP-Wahrheitsvergleich normalisiert.","examples":[true]},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string","description":"Optionaler SHA-256-Inhaltshash (64 Hexzeichen)."}},"additionalProperties":true},"replay":{"type":"object","properties":{"capturedAt":{"type":"string"},"meta":{},"eventCount":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true,"minProperties":1}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Supportbericht und signierte Anhang-Uploads vorbereiten","value":{"data":{"payloadVersion":2,"trigger":"manual","feedbackType":"bug","comment":"Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.","route":{"name":"offers","path":"/offers","fullPath":"/offers"},"appContext":{"appVersion":"example"}}}}}}}},"x-edoobox-upload-ticket":{"provider":"gcs","bucketEnv":"ED_BUCKET_PRIVATE_NAME","objectPrefixEnv":"ED_BUCKET_PRIVATE_PREFIX","urlPointer":"/artifacts/*/uploadUrl","pathPrefixTemplate":"/filemanager/{account_id}/feedback/","method":"PUT","maxBytes":52428800,"imageMaxBytes":8388608,"expiresInSeconds":1800,"mimeTypePointer":"/artifacts/*/mimeType","byteSizePointer":"/artifacts/*/byteSize","source":{"file":"application/models/Edsupportfeedbackmodel.php","line":1644},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"upload-session","finalization":{"operationId":"post_v2_support_error_feedback_reports_placeholder_finalize","argumentBindings":[{"target":"/body/data/reportId","source":"/reportId"}],"scope":"upload-session","after":"all-requested-artifact-transfers"},"successCriteria":{"responsePointers":{"/error":false},"artifacts":{"selection":"all-requested-artifact-keys","everyStatus":"stored"}},"notes":["One prepare can return several upload tickets. Retain the top-level reportId and match each requested artifact by its opaque key.","Finalize the report only after all intended artifact transfers. Native finalization independently checks storage, MIME, byte size and optional SHA256.","Top-level status stored is insufficient: every requested artifact key must be returned with its own status stored.","There is no account-JWT report read or abort operation. Never invent one or automatically repeat a mutation after an ambiguous outcome."]}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Support.php","method":"postSupport","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"support","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/support/error-feedback-reports-placeholder/upload Screenshot eines vorbereiteten Berichts hochladen Speichert visualScreenshot oder annotatedScreenshot eines eigenen vorbereiteten Berichts als Alternative zum direkten Objektspeicher-Upload. reportId (oder feedbackKey), artifactKey und für noch nicht gespeicherte Anhänge dataUrl sind erforderlich. MIME, positive Größe bis 8 MiB sowie erwartete Größe/Hash müssen passen. Bereits gespeicherte Anhänge werden unverändert bestätigt; Replay ist hier nicht unterstützt. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. - Operation-ID: post_v2_support_error_feedback_reports_placeholder_upload - Dokumentation: /docs/api/v2/operations/post-v2-support-error-feedback-reports-placeholder-upload-68729f041cd3/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "reportId": "efb_syntheticexample", "artifactKey": "efa_syntheticexample", "dataUrl": "data:image/png;base64,iVBORw0KGgo=" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/support/error-feedback-reports-placeholder/upload' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/support/error-feedback-reports-placeholder/upload"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "reportId": "efb_syntheticexample", "artifactKey": "efa_syntheticexample", "dataUrl": "data:image/png;base64,iVBORw0KGgo=" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/support/error-feedback-reports-placeholder/upload" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"reportId\":\"efb_syntheticexample\",\"artifactKey\":\"efa_syntheticexample\",\"dataUrl\":\"data:image/png;base64,iVBORw0KGgo=\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_support_error_feedback_reports_placeholder_upload","summary":"Screenshot eines vorbereiteten Berichts hochladen","description":"Speichert visualScreenshot oder annotatedScreenshot eines eigenen vorbereiteten Berichts als Alternative zum direkten Objektspeicher-Upload. reportId (oder feedbackKey), artifactKey und für noch nicht gespeicherte Anhänge dataUrl sind erforderlich. MIME, positive Größe bis 8 MiB sowie erwartete Größe/Hash müssen passen. Bereits gespeicherte Anhänge werden unverändert bestätigt; Replay ist hier nicht unterstützt. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden.","tags":["support"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Berichts-/Anhangstatus stehen auf oberster Ebene, nicht unter data. Tabellen-/Speicherfehler können ebenfalls HTTP 200 verwenden. artifacts.status muss für jeden angeforderten Anhang geprüft werden.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"const":true},"error":{"const":false},"key":{"type":"string"},"status":{"type":"string"},"type":{"type":"string","enum":["visualScreenshot","annotatedScreenshot"]}},"additionalProperties":true,"required":["success","error"]},{"type":"object","properties":{"success":{"const":false},"error":{"type":"string"},"error_code":{"type":"string"}},"additionalProperties":true,"required":["error"]}]},"example":{"success":true,"error":false,"key":"efa_syntheticexample","status":"stored","type":"visualScreenshot"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"reportId":{"type":"string","description":"Berichtsschlüssel aus prepare/create."},"feedbackKey":{"type":"string","description":"Alias für reportId."},"artifactKey":{"type":"string"},"dataUrl":{"type":"string","description":"Base64-dataUrl des vorbereiteten Bildes."}},"additionalProperties":true,"anyOf":[{"required":["reportId"]},{"required":["feedbackKey"]}],"required":["artifactKey"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Screenshot eines vorbereiteten Berichts hochladen","value":{"data":{"reportId":"efb_syntheticexample","artifactKey":"efa_syntheticexample","dataUrl":"data:image/png;base64,iVBORw0KGgo="}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Support.php","method":"postSupport","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"support","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/support/error-feedback-reports/finalize Meldungsanhänge prüfen und abschließen Prüft vorbereitete Anhänge eines eigenen Berichts auf Existenz, MIME, Größe und optionalen Hash und aktualisiert die Speicherstatus. reportId oder Alias feedbackKey ist erforderlich. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. Berichte mit route.name=pageRegister oder /pages/register werden vom Controller deaktiviert; dann antwortet data.disabled=true mit reason=signup_error_feedback_disabled. - Operation-ID: post_v2_support_error_feedback_reports_finalize - Dokumentation: /docs/api/v2/operations/post-v2-support-error-feedback-reports-finalize-dcef4a7b2de9/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "reportId": "efb_syntheticexample" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/support/error-feedback-reports/finalize' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/support/error-feedback-reports/finalize"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "reportId": "efb_syntheticexample" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/support/error-feedback-reports/finalize" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"reportId\":\"efb_syntheticexample\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_support_error_feedback_reports_finalize","summary":"Meldungsanhänge prüfen und abschließen","description":"Prüft vorbereitete Anhänge eines eigenen Berichts auf Existenz, MIME, Größe und optionalen Hash und aktualisiert die Speicherstatus. reportId oder Alias feedbackKey ist erforderlich. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. Berichte mit route.name=pageRegister oder /pages/register werden vom Controller deaktiviert; dann antwortet data.disabled=true mit reason=signup_error_feedback_disabled.","tags":["support"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Berichts-/Anhangstatus stehen auf oberster Ebene, nicht unter data. Tabellen-/Speicherfehler können ebenfalls HTTP 200 verwenden. artifacts.status muss für jeden angeforderten Anhang geprüft werden.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"const":true},"error":{"const":false},"status":{"const":"stored"},"reportId":{"type":"string"},"receivedAt":{"type":"string","format":"date-time"},"artifacts":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"byteSize":{"type":"integer"}},"additionalProperties":true,"required":["key","type","status","byteSize"]}}},"additionalProperties":true,"required":["success","error"]},{"type":"object","properties":{"success":{"const":false},"error":{"type":"string"},"error_code":{"type":"string"}},"additionalProperties":true,"required":["error"]}]},"example":{"success":true,"error":false,"status":"stored","reportId":"efb_syntheticexample","receivedAt":"2027-04-01T10:00:00+00:00","artifacts":[]}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"reportId":{"type":"string","description":"Berichtsschlüssel aus prepare/create."},"feedbackKey":{"type":"string","description":"Alias für reportId."}},"additionalProperties":true,"anyOf":[{"required":["reportId"]},{"required":["feedbackKey"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Meldungsanhänge prüfen und abschließen","value":{"data":{"reportId":"efb_syntheticexample"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Support.php","method":"postSupport","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"support","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/support/error-feedback-reports/prepare Supportbericht und signierte Anhang-Uploads vorbereiten Speichert Bericht und Anhang-Metadaten und erzeugt signierte PUT-URLs für 30 Minuten. Bilder benötigen positive byteSize bis 8 MiB und MIME image/png, image/jpeg oder image/webp; Replay benötigt positive byteSize bis 50 MiB. sha256 ist optional. Nach dem Byte-Upload finalize aufrufen; bei Screenshot-CORS-Problemen steht upload zur Verfügung. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. Berichte mit route.name=pageRegister oder /pages/register werden vom Controller deaktiviert; dann antwortet data.disabled=true mit reason=signup_error_feedback_disabled. - Operation-ID: post_v2_support_error_feedback_reports_prepare - Dokumentation: /docs/api/v2/operations/post-v2-support-error-feedback-reports-prepare-15ef627bb3f6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "payloadVersion": 2, "trigger": "manual", "feedbackType": "bug", "comment": "Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.", "route": { "name": "offers", "path": "/offers", "fullPath": "/offers" }, "appContext": { "appVersion": "example" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/support/error-feedback-reports/prepare' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/support/error-feedback-reports/prepare"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "payloadVersion": 2, "trigger": "manual", "feedbackType": "bug", "comment": "Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.", "route": { "name": "offers", "path": "/offers", "fullPath": "/offers" }, "appContext": { "appVersion": "example" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/support/error-feedback-reports/prepare" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"payloadVersion\":2,\"trigger\":\"manual\",\"feedbackType\":\"bug\",\"comment\":\"Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.\",\"route\":{\"name\":\"offers\",\"path\":\"/offers\",\"fullPath\":\"/offers\"},\"appContext\":{\"appVersion\":\"example\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_support_error_feedback_reports_prepare","summary":"Supportbericht und signierte Anhang-Uploads vorbereiten","description":"Speichert Bericht und Anhang-Metadaten und erzeugt signierte PUT-URLs für 30 Minuten. Bilder benötigen positive byteSize bis 8 MiB und MIME image/png, image/jpeg oder image/webp; Replay benötigt positive byteSize bis 50 MiB. sha256 ist optional. Nach dem Byte-Upload finalize aufrufen; bei Screenshot-CORS-Problemen steht upload zur Verfügung. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. Berichte mit route.name=pageRegister oder /pages/register werden vom Controller deaktiviert; dann antwortet data.disabled=true mit reason=signup_error_feedback_disabled.","tags":["support"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Berichts-/Anhangstatus stehen auf oberster Ebene, nicht unter data. Tabellen-/Speicherfehler können ebenfalls HTTP 200 verwenden. artifacts.status muss für jeden angeforderten Anhang geprüft werden.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"const":true},"error":{"const":false},"status":{"const":"prepared"},"reportId":{"type":"string","description":"Öffentlicher Berichtsschlüssel efb_…"},"receivedAt":{"type":"string","format":"date-time"},"artifacts":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"byteSize":{"type":"integer"},"mimeType":{"type":"string"},"uploadUrl":{"type":"string","description":"Kurzlebige signierte PUT-URL; vertraulich behandeln."}},"additionalProperties":true}},"uploadExpiresAt":{"type":"string","format":"date-time"}},"additionalProperties":true,"required":["success","error"]},{"type":"object","properties":{"success":{"const":false},"error":{"type":"string"},"error_code":{"type":"string"}},"additionalProperties":true,"required":["error"]}]},"example":{"success":true,"error":false,"status":"prepared","reportId":"efb_syntheticexample","receivedAt":"2027-04-01T10:00:00+00:00","artifacts":[],"uploadExpiresAt":"2027-04-01T10:30:00+00:00"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"payloadVersion":{"type":["integer","string"],"description":"Auf 1–99 normalisiert; Standard 1 bei create, 2 bei prepare."},"trigger":{"type":"string","description":"manual erzeugt unread, sonst automatic."},"feedbackType":{"description":"idea bzw. idee normalisiert zu idea; alle anderen Werte werden bug.","examples":["bug"]},"feedback_type":{"description":"Alias für feedbackType."},"comment":{"description":"Kommentar; auf 6000 Zeichen normalisiert.","x-edoobox-canonicalType":"string"},"appContext":{"type":"object","properties":{"accountId":{"type":"string","pattern":"^account_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["account_012345abcdef_123456"]},"adminId":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["admin_012345abcdef_123456"]},"APP_VERSION":{"type":"string"},"appVersion":{"type":"string"},"environment":{"type":"string"},"userAgent":{"type":"string"}},"additionalProperties":true},"route":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"fullPath":{"type":"string"}},"additionalProperties":true},"telemetry":{"type":"object","properties":{"viewport":{}},"additionalProperties":true},"annotations":{},"visualScreenshot":{"type":"object","properties":{"mimeType":{"type":"string","description":"Bei create aus dataUrl abgeleitet; bei prepare image/png, image/jpeg oder image/webp."},"width":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"height":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"capturedAt":{"type":"string"},"source":{"type":"string"},"redactedByAnnotatedScreenshot":{"description":"Wird per PHP-Wahrheitsvergleich normalisiert.","examples":[true]},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string","description":"Optionaler SHA-256-Inhaltshash (64 Hexzeichen)."}},"additionalProperties":true},"annotatedScreenshot":{"type":"object","properties":{"mimeType":{"type":"string","description":"Bei create aus dataUrl abgeleitet; bei prepare image/png, image/jpeg oder image/webp."},"width":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"height":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"capturedAt":{"type":"string"},"source":{"type":"string"},"redactedByAnnotatedScreenshot":{"description":"Wird per PHP-Wahrheitsvergleich normalisiert.","examples":[true]},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string","description":"Optionaler SHA-256-Inhaltshash (64 Hexzeichen)."}},"additionalProperties":true},"replay":{"type":"object","properties":{"capturedAt":{"type":"string"},"meta":{},"eventCount":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byteSize":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"byte_size":{"type":["integer","string"],"description":"Wird zur nichtnegativen Ganzzahl normalisiert."},"sha256":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true,"minProperties":1}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Supportbericht und signierte Anhang-Uploads vorbereiten","value":{"data":{"payloadVersion":2,"trigger":"manual","feedbackType":"bug","comment":"Die Angebotsansicht zeigt eine unerwartete Fehlermeldung.","route":{"name":"offers","path":"/offers","fullPath":"/offers"},"appContext":{"appVersion":"example"}}}}}}}},"x-edoobox-upload-ticket":{"provider":"gcs","bucketEnv":"ED_BUCKET_PRIVATE_NAME","objectPrefixEnv":"ED_BUCKET_PRIVATE_PREFIX","urlPointer":"/artifacts/*/uploadUrl","pathPrefixTemplate":"/filemanager/{account_id}/feedback/","method":"PUT","maxBytes":52428800,"imageMaxBytes":8388608,"expiresInSeconds":1800,"mimeTypePointer":"/artifacts/*/mimeType","byteSizePointer":"/artifacts/*/byteSize","source":{"file":"application/models/Edsupportfeedbackmodel.php","line":1644},"workflow":{"metadataCreatedBeforeTransfer":true,"requiresApiFinalization":true,"scope":"upload-session","finalization":{"operationId":"post_v2_support_error_feedback_reports_finalize","argumentBindings":[{"target":"/body/data/reportId","source":"/reportId"}],"scope":"upload-session","after":"all-requested-artifact-transfers"},"successCriteria":{"responsePointers":{"/error":false},"artifacts":{"selection":"all-requested-artifact-keys","everyStatus":"stored"}},"notes":["One prepare can return several upload tickets. Retain the top-level reportId and match each requested artifact by its opaque key.","Finalize the report only after all intended artifact transfers. Native finalization independently checks storage, MIME, byte size and optional SHA256.","Top-level status stored is insufficient: every requested artifact key must be returned with its own status stored.","There is no account-JWT report read or abort operation. Never invent one or automatically repeat a mutation after an ambiguous outcome."]}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Support.php","method":"postSupport","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"support","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/support/error-feedback-reports/upload Screenshot eines vorbereiteten Berichts hochladen Speichert visualScreenshot oder annotatedScreenshot eines eigenen vorbereiteten Berichts als Alternative zum direkten Objektspeicher-Upload. reportId (oder feedbackKey), artifactKey und für noch nicht gespeicherte Anhänge dataUrl sind erforderlich. MIME, positive Größe bis 8 MiB sowie erwartete Größe/Hash müssen passen. Bereits gespeicherte Anhänge werden unverändert bestätigt; Replay ist hier nicht unterstützt. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. Berichte mit route.name=pageRegister oder /pages/register werden vom Controller deaktiviert; dann antwortet data.disabled=true mit reason=signup_error_feedback_disabled. - Operation-ID: post_v2_support_error_feedback_reports_upload - Dokumentation: /docs/api/v2/operations/post-v2-support-error-feedback-reports-upload-2fbc1167e033/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "reportId": "efb_syntheticexample", "artifactKey": "efa_syntheticexample", "dataUrl": "data:image/png;base64,iVBORw0KGgo=" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/support/error-feedback-reports/upload' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/support/error-feedback-reports/upload"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "reportId": "efb_syntheticexample", "artifactKey": "efa_syntheticexample", "dataUrl": "data:image/png;base64,iVBORw0KGgo=" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/support/error-feedback-reports/upload" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"reportId\":\"efb_syntheticexample\",\"artifactKey\":\"efa_syntheticexample\",\"dataUrl\":\"data:image/png;base64,iVBORw0KGgo=\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_support_error_feedback_reports_upload","summary":"Screenshot eines vorbereiteten Berichts hochladen","description":"Speichert visualScreenshot oder annotatedScreenshot eines eigenen vorbereiteten Berichts als Alternative zum direkten Objektspeicher-Upload. reportId (oder feedbackKey), artifactKey und für noch nicht gespeicherte Anhänge dataUrl sind erforderlich. MIME, positive Größe bis 8 MiB sowie erwartete Größe/Hash müssen passen. Bereits gespeicherte Anhänge werden unverändert bestätigt; Replay ist hier nicht unterstützt. Die placeholder-Variante nutzt dieselbe Modellimplementierung. error=false bestätigt nicht automatisch erfolgreiche Anhänge; deren status muss ausgewertet werden. Berichte mit route.name=pageRegister oder /pages/register werden vom Controller deaktiviert; dann antwortet data.disabled=true mit reason=signup_error_feedback_disabled.","tags":["support"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Berichts-/Anhangstatus stehen auf oberster Ebene, nicht unter data. Tabellen-/Speicherfehler können ebenfalls HTTP 200 verwenden. artifacts.status muss für jeden angeforderten Anhang geprüft werden.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"const":true},"error":{"const":false},"key":{"type":"string"},"status":{"type":"string"},"type":{"type":"string","enum":["visualScreenshot","annotatedScreenshot"]}},"additionalProperties":true,"required":["success","error"]},{"type":"object","properties":{"success":{"const":false},"error":{"type":"string"},"error_code":{"type":"string"}},"additionalProperties":true,"required":["error"]}]},"example":{"success":true,"error":false,"key":"efa_syntheticexample","status":"stored","type":"visualScreenshot"}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"reportId":{"type":"string","description":"Berichtsschlüssel aus prepare/create."},"feedbackKey":{"type":"string","description":"Alias für reportId."},"artifactKey":{"type":"string"},"dataUrl":{"type":"string","description":"Base64-dataUrl des vorbereiteten Bildes."}},"additionalProperties":true,"anyOf":[{"required":["reportId"]},{"required":["feedbackKey"]}],"required":["artifactKey"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Screenshot eines vorbereiteten Berichts hochladen","value":{"data":{"reportId":"efb_syntheticexample","artifactKey":"efa_syntheticexample","dataUrl":"data:image/png;base64,iVBORw0KGgo="}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Support.php","method":"postSupport","line":10},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"support","short":null,"checkedByDispatcher":false,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/tag Angebots-Tag löschen Löscht Angebots-Tag dauerhaft aus der Tag-Tabelle. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang. - Operation-ID: delete_v2_tag - Dokumentation: /docs/api/v2/operations/delete-v2-tag-f06f0242979f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "tag_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/tag' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "tag_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"tag_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_tag","summary":"Angebots-Tag löschen","description":"Löscht Angebots-Tag dauerhaft aus der Tag-Tabelle. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"description":"Öffentliche Objekt-ID; erforderlich, wenn sie nicht als id im JSON-Envelope gesendet wird."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id ist die öffentliche ID; delete zeigt eine betroffene Zeile an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"tag_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Angebots-Tag löschen","value":{"id":"tag_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"deleteTag","line":103},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/tag Angebots-Tag auflisten Liest Angebots-Tag-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_tag - Dokumentation: /docs/api/v2/operations/get-v2-tag-9da7a106d106/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/tag' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_tag","summary":"Angebots-Tag auflisten","description":"Liest Angebots-Tag-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"tag_012345abcdef_123456":{"id":"tag_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"},"value":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"},"value":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"getTag","line":119},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/tag Angebots-Tag erstellen Erstellt Angebots-Tag aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft. - Operation-ID: post_v2_tag - Dokumentation: /docs/api/v2/operations/post-v2-tag-0a0a9d87ff8f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/tag' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_tag","summary":"Angebots-Tag erstellen","description":"Erstellt Angebots-Tag aus data. Ein Objekt erzeugt einen Eintrag, eine Liste mehrere Einträge; Pflichtfelder gelten je Eintrag. Konto- und Objektzugehörigkeit werden serverseitig geprüft.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält eine öffentliche ID oder bei Stapelerstellung eine ID-Liste; insert bestätigt die Speicherung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"tag_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]}}]},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":35},"value":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":48}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":35},"value":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":48}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["name"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"data":{"name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"postTag","line":69},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/tag Angebots-Tag ändern Ändert die übergebenen veränderbaren Felder von Angebots-Tag. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. - Operation-ID: put_v2_tag - Dokumentation: /docs/api/v2/operations/put-v2-tag-871841379c66/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" }, "id": "tag_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/tag' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" }, "id": "tag_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"},\"id\":\"tag_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_tag","summary":"Angebots-Tag ändern","description":"Ändert die übergebenen veränderbaren Felder von Angebots-Tag. Die v2-ID muss als id im JSON-Envelope stehen. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"tag_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":35},"value":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":48}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Angebots-Tag ändern","value":{"data":{"name":"Example"},"id":"tag_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"updateTag","line":86},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/tag/{id} Angebots-Tag löschen Löscht Angebots-Tag dauerhaft aus der Tag-Tabelle. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang. - Operation-ID: delete_v2_tag_id - Dokumentation: /docs/api/v2/operations/delete-v2-tag-id-b79ea61c7847/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "tag_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/tag/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "tag_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"tag_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_tag_id","summary":"Angebots-Tag löschen","description":"Löscht Angebots-Tag dauerhaft aus der Tag-Tabelle. Die v2-ID steht im Pfad oder als id im Query-/JSON-Envelope; id im JSON hat Vorrang.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id ist die öffentliche ID; delete zeigt eine betroffene Zeile an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"tag_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]}},"additionalProperties":true,"description":"Optionaler DELETE-JSON-Envelope. id überschreibt einen id-Queryparameter bzw. die Pfad-ID; data wird nicht ausgewertet."},"examples":{"request":{"summary":"Angebots-Tag löschen","value":{"id":"tag_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"deleteTag","line":103},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/tag/{id} Angebots-Tag lesen Liest Angebots-Tag anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_tag_id - Dokumentation: /docs/api/v2/operations/get-v2-tag-id-1886482fa5da/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/tag/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_tag_id","summary":"Angebots-Tag lesen","description":"Liest Angebots-Tag anhand der öffentlichen v2-ID. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"tag_012345abcdef_123456":{"id":"tag_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"},"value":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"},"value":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"getTag","line":119},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/tag/{id} Angebots-Tag ändern Ändert die übergebenen veränderbaren Felder von Angebots-Tag. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert. - Operation-ID: put_v2_tag_id - Dokumentation: /docs/api/v2/operations/put-v2-tag-id-7a3ddbfcc951/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "name": "Example" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/tag/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "name": "Example" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"name\":\"Example\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_tag_id","summary":"Angebots-Tag ändern","description":"Ändert die übergebenen veränderbaren Felder von Angebots-Tag. Die v2-ID steht im Pfad; id im JSON-Envelope hat Vorrang. Nicht übergebene Felder bleiben erhalten; unveränderbare Felder werden vom Legacy-Server ignoriert.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die öffentliche ID; update kann bei ausbleibender Änderung false sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"tag_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":35},"value":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":48}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["tag_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Angebots-Tag ändern","value":{"data":{"name":"Example"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"updateTag","line":86},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/tag/list Angebots-Tag auflisten Liest Angebots-Tag-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung. - Operation-ID: get_v2_tag_list - Dokumentation: /docs/api/v2/operations/get-v2-tag-list-6f00b1635a0f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/tag/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_tag_list","summary":"Angebots-Tag auflisten","description":"Liest Angebots-Tag-Datensätze des gewählten Kontos. fields, filter, order und limit steuern die Auswahl. Einzelabfragen bleiben Sammlungen; response_type=array liefert eine Liste, sonst wird nach öffentlicher ID indiziert. Leere Ergebnisse sind []. filter kann offer mit einer Angebots-v2-ID enthalten; offer/sort/type erscheinen nur bei entsprechender Angebotsverknüpfung.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"tag_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine Sammlung; limit.start/reply/total enthält Paginierung und Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"tag_012345abcdef_123456":{"id":"tag_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"},"value":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"},"value":{"type":"string"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["offer_012345abcdef_123456"]},"type":{"type":"string","enum":["normal","include","exclude"]}},"additionalProperties":true,"required":["id"]}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"getTag","line":119},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/tag/list/connected_tags Verwendete Tags verbundener Konten lesen Liest Tags verbundener Konten, die dort als normaler Angebots-Tag verwendet werden. Das eigene Konto wird ausgeschlossen. filterValue enthält name bzw. name/value. Die vollständige Auswahl ist nach Konto, Name und Wert sortiert; allgemeine Filter und Paginierung werden ignoriert. - Operation-ID: get_v2_tag_list_connected_tags - Dokumentation: /docs/api/v2/operations/get-v2-tag-list-connected-tags-3bc4b73d7d33/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/tag/list/connected_tags' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/tag/list/connected_tags"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/tag/list/connected_tags" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_tag_list_connected_tags","summary":"Verwendete Tags verbundener Konten lesen","description":"Liest Tags verbundener Konten, die dort als normaler Angebots-Tag verwendet werden. Das eigene Konto wird ausgeschlossen. filterValue enthält name bzw. name/value. Die vollständige Auswahl ist nach Konto, Name und Wert sortiert; allgemeine Filter und Paginierung werden ignoriert.","tags":["tag"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist eine ID-indizierte Sammlung, [] bei keiner Verbindung oder keinem Treffer; limit enthält die vollständige Trefferzahl.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"tag_012345abcdef_123456":{"id":"tag_012345abcdef_123456","name":"Niveau","value":"Einsteiger","filterValue":"Niveau/Einsteiger","connected":true,"subtitle":"Connected: BEISPIEL","accountShortcut":"BEISPIEL"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"},"value":{"type":"string"},"filterValue":{"type":"string"},"connected":{"const":true},"subtitle":{"type":"string"},"accountShortcut":{"type":"string"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID; Beispiel ist synthetisch.","examples":["tag_012345abcdef_123456"]},"name":{"type":"string"},"value":{"type":"string"},"filterValue":{"type":"string"},"connected":{"const":true},"subtitle":{"type":"string"},"accountShortcut":{"type":"string"}},"additionalProperties":true},"maxItems":0}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Tag.php","line":172},"reason":"SELECT related tags."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/10-other-resources.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Tag.php","method":"getTag","line":119,"branch":{"file":"application/models/v2/Tag.php","line":124}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"tag","short":"tag","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/task Geplante aufgabe entfernen Markiert die Aufgabe als gelöscht und ergänzt die verknüpfte Notiz um den Löschhinweis. Bereits gelöschte oder nicht gefundene Aufgaben ergeben delete=false. Die öffentliche id ist als Query-Parameter oder im JSON-Umschlag erforderlich. - Operation-ID: delete_v2_task - Dokumentation: /docs/api/v2/operations/delete-v2-task-77688f21e6e4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Alternativ zu id im JSON-Umschlag; einer dieser Selektoren ist erforderlich. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["task_012345abcdef_123456"]},"description":"Alternativ zu id im JSON-Umschlag; einer dieser Selektoren ist erforderlich."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "task_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/task' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "task_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"task_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_task","summary":"Geplante aufgabe entfernen","description":"Markiert die Aufgabe als gelöscht und ergänzt die verknüpfte Notiz um den Löschhinweis. Bereits gelöschte oder nicht gefundene Aufgaben ergeben delete=false. Die öffentliche id ist als Query-Parameter oder im JSON-Umschlag erforderlich.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["task_012345abcdef_123456"]},"description":"Alternativ zu id im JSON-Umschlag; einer dieser Selektoren ist erforderlich."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche ID und Löschstatus; delete=false bedeutet, dass kein Datensatz entfernt wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"task_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Geplante aufgabe entfernen","value":{"id":"task_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"deleteTask","line":376},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/task Geplante Aufgaben auflisten Liest noch nicht gelöschte geplante Aufgaben mit Funktion, Empfängerauswahl und Ausführungsstatus; zeigt auch bereits ausgeführte Aufgaben, solange sie nicht gelöscht wurden. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_task - Dokumentation: /docs/api/v2/operations/get-v2-task-d2b755a5e689/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/task' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_task","summary":"Geplante Aufgaben auflisten","description":"Liest noch nicht gelöschte geplante Aufgaben mit Funktion, Empfängerauswahl und Ausführungsstatus; zeigt auch bereits ausgeführte Aufgaben, solange sie nicht gelöscht wurden. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"task_012345abcdef_123456":{"id":"task_012345abcdef_123456","offer":"offer_012345abcdef_123456","admin":"admin_012345abcdef_123456","task_name":"Erinnerung senden","task_func":{"func":"mail","id":1},"date_run":"2026-11-01T09:00:00+01:00","status":"pending","user_all":true}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"getTask","line":393},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/task Geplante aufgabe anlegen Plant eine E-Mail-, Finanz- oder Dokumentaufgabe für ein Angebot und legt eine verknüpfte Notiz an. offer, admin, task_func, task_name und date_run sind Pflicht. Zusätzlich user_all=true oder eine gültige user-Auswahl übergeben; die Legacy-Auswahlprüfung verlangt intern eine mehr als drei Zeichen lange ID-Liste. Ein zu früher Termin wird auf mindestens jetzt + 180 Sekunden verschoben. after_date_run ist nur für eigene E-Mail-Vorlagen erlaubt, nicht für eingebaute mail/financ/job-Funktionen. Eigene Vorlagen müssen E-Mail-Vorlagen sein. data erlaubt ein einzelnes Objekt oder höchstens 100 Objekte als Batch; die Antwort enthält eine ID bzw. eine ID-Liste. - Operation-ID: post_v2_task - Dokumentation: /docs/api/v2/operations/post-v2-task-fcc01c322153/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "offer": "offer_012345abcdef_123456", "admin": "admin_012345abcdef_123456", "task_func": { "func": "mail", "id": 1 }, "task_name": "Erinnerung senden", "date_run": "2026-11-01T09:00:00+01:00", "user_all": true } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/task' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "offer": "offer_012345abcdef_123456", "admin": "admin_012345abcdef_123456", "task_func": { "func": "mail", "id": 1 }, "task_name": "Erinnerung senden", "date_run": "2026-11-01T09:00:00+01:00", "user_all": true } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"offer\":\"offer_012345abcdef_123456\",\"admin\":\"admin_012345abcdef_123456\",\"task_func\":{\"func\":\"mail\",\"id\":1},\"task_name\":\"Erinnerung senden\",\"date_run\":\"2026-11-01T09:00:00+01:00\",\"user_all\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_task","summary":"Geplante aufgabe anlegen","description":"Plant eine E-Mail-, Finanz- oder Dokumentaufgabe für ein Angebot und legt eine verknüpfte Notiz an. offer, admin, task_func, task_name und date_run sind Pflicht. Zusätzlich user_all=true oder eine gültige user-Auswahl übergeben; die Legacy-Auswahlprüfung verlangt intern eine mehr als drei Zeichen lange ID-Liste. Ein zu früher Termin wird auf mindestens jetzt + 180 Sekunden verschoben. after_date_run ist nur für eigene E-Mail-Vorlagen erlaubt, nicht für eingebaute mail/financ/job-Funktionen. Eigene Vorlagen müssen E-Mail-Vorlagen sein. data erlaubt ein einzelnes Objekt oder höchstens 100 Objekte als Batch; die Antwort enthält eine ID bzw. eine ID-Liste.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue öffentliche ID oder beim Batch eine Liste; insert zeigt das Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"task_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"note":{"type":"string","pattern":"^note_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche emailtemplate_-ID einer verwendbaren E-Mail-Vorlagenfamilie desselben Kontos. Diese Ausnahme verwendet eine v2-ID statt eines eingebauten numerischen Funktionscodes; PDF-Vorlagen sind unzulässig.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]}],"description":"Built-in task IDs are function selectors, not database record IDs. Email-template tasks require a public emailtemplate ID for a usable mail template. Die Zahlencodes gelten ausschließlich zusammen mit ihrem jeweiligen func; eigene E-Mail-Vorlagen verwenden eine öffentliche emailtemplate_-ID."},"task_name":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Erinnerung senden"]},"date_run":{"type":["string","number","boolean","null"],"description":"Datum/Zeit als durch PHP strtotime lesbarer String empfohlen (ISO 8601). Ungültige Werte ergeben intern 0; bei Erstellung wird mindestens jetzt + 180 Sekunden gesetzt."},"status":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 30 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["pending"]},"user":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"user_all":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"after_date_run":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[false]}},"additionalProperties":true,"required":["offer","admin","task_func","task_name","date_run"],"anyOf":[{"required":["user_all"]},{"required":["user"],"properties":{"user":{"minItems":1}}}]},{"type":"array","items":{"type":"object","properties":{"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"admin":{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"note":{"type":"string","pattern":"^note_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche emailtemplate_-ID einer verwendbaren E-Mail-Vorlagenfamilie desselben Kontos. Diese Ausnahme verwendet eine v2-ID statt eines eingebauten numerischen Funktionscodes; PDF-Vorlagen sind unzulässig.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]}],"description":"Built-in task IDs are function selectors, not database record IDs. Email-template tasks require a public emailtemplate ID for a usable mail template. Die Zahlencodes gelten ausschließlich zusammen mit ihrem jeweiligen func; eigene E-Mail-Vorlagen verwenden eine öffentliche emailtemplate_-ID."},"task_name":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Erinnerung senden"]},"date_run":{"type":["string","number","boolean","null"],"description":"Datum/Zeit als durch PHP strtotime lesbarer String empfohlen (ISO 8601). Ungültige Werte ergeben intern 0; bei Erstellung wird mindestens jetzt + 180 Sekunden gesetzt."},"status":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 30 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["pending"]},"user":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"user_all":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"after_date_run":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[false]}},"additionalProperties":true,"required":["offer","admin","task_func","task_name","date_run"],"anyOf":[{"required":["user_all"]},{"required":["user"],"properties":{"user":{"minItems":1}}}]},"minItems":1,"maxItems":100}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Geplante aufgabe anlegen","value":{"data":{"offer":"offer_012345abcdef_123456","admin":"admin_012345abcdef_123456","task_func":{"func":"mail","id":1},"task_name":"Erinnerung senden","date_run":"2026-11-01T09:00:00+01:00","user_all":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"postTask","line":342},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/task Geplante aufgabe ändern Ändert Name, Termin oder nachträgliche Ausführung einer geplanten Aufgabe. Funktion, Angebot, Admin und Empfängerauswahl bleiben unveränderlich. Wenn after_date_run im Update enthalten ist, aktiviert der Legacy-Hook diese Eigenschaft für eigene E-Mail-Vorlagen unabhängig vom übergebenen Wahrheitswert; eingebaute mail/financ/job-Funktionen lehnen dies ab. id im JSON-Umschlag ist Pflicht. - Operation-ID: put_v2_task - Dokumentation: /docs/api/v2/operations/put-v2-task-7d5de58df8ae/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "task_name": "Erinnerung am Vorabend", "date_run": "2026-11-01T18:00:00+01:00" }, "id": "task_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/task' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "task_name": "Erinnerung am Vorabend", "date_run": "2026-11-01T18:00:00+01:00" }, "id": "task_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"task_name\":\"Erinnerung am Vorabend\",\"date_run\":\"2026-11-01T18:00:00+01:00\"},\"id\":\"task_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_task","summary":"Geplante aufgabe ändern","description":"Ändert Name, Termin oder nachträgliche Ausführung einer geplanten Aufgabe. Funktion, Angebot, Admin und Empfängerauswahl bleiben unveränderlich. Wenn after_date_run im Update enthalten ist, aktiviert der Legacy-Hook diese Eigenschaft für eigene E-Mail-Vorlagen unabhängig vom übergebenen Wahrheitswert; eingebaute mail/financ/job-Funktionen lehnen dies ab. id im JSON-Umschlag ist Pflicht.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, update das tatsächliche Änderungsergebnis; update=false ist keine Bestätigung einer Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"task_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"task_name":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Erinnerung senden"]},"date_run":{"type":["string","number","boolean","null"],"description":"Datum/Zeit als durch PHP strtotime lesbarer String empfohlen (ISO 8601). Ungültige Werte ergeben intern 0; bei Erstellung wird mindestens jetzt + 180 Sekunden gesetzt."},"after_date_run":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[false]}},"additionalProperties":true},"id":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Geplante aufgabe ändern","value":{"data":{"task_name":"Erinnerung am Vorabend","date_run":"2026-11-01T18:00:00+01:00"},"id":"task_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"updateTask","line":359},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/task/{id} Geplante aufgabe entfernen Markiert die Aufgabe als gelöscht und ergänzt die verknüpfte Notiz um den Löschhinweis. Bereits gelöschte oder nicht gefundene Aufgaben ergeben delete=false. Die öffentliche ID im Pfad wählt das Objekt. - Operation-ID: delete_v2_task_id - Dokumentation: /docs/api/v2/operations/delete-v2-task-id-1c9e0366fa8e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "task_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/task/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "task_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"task_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_task_id","summary":"Geplante aufgabe entfernen","description":"Markiert die Aufgabe als gelöscht und ergänzt die verknüpfte Notiz um den Löschhinweis. Bereits gelöschte oder nicht gefundene Aufgaben ergeben delete=false. Die öffentliche ID im Pfad wählt das Objekt.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche ID und Löschstatus; delete=false bedeutet, dass kein Datensatz entfernt wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"task_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true},"examples":{"request":{"summary":"Geplante aufgabe entfernen","value":{"id":"task_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"deleteTask","line":376},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/task/{id} Geplante aufgabe lesen Liest noch nicht gelöschte geplante Aufgaben mit Funktion, Empfängerauswahl und Ausführungsstatus; zeigt auch bereits ausgeführte Aufgaben, solange sie nicht gelöscht wurden. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_task_id - Dokumentation: /docs/api/v2/operations/get-v2-task-id-65a0258f2c61/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/task/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_task_id","summary":"Geplante aufgabe lesen","description":"Liest noch nicht gelöschte geplante Aufgaben mit Funktion, Empfängerauswahl und Ausführungsstatus; zeigt auch bereits ausgeführte Aufgaben, solange sie nicht gelöscht wurden. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"task_012345abcdef_123456":{"id":"task_012345abcdef_123456","offer":"offer_012345abcdef_123456","admin":"admin_012345abcdef_123456","task_name":"Erinnerung senden","task_func":{"func":"mail","id":1},"date_run":"2026-11-01T09:00:00+01:00","status":"pending","user_all":true}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"getTask","line":393},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/task/{id} Geplante aufgabe ändern Ändert Name, Termin oder nachträgliche Ausführung einer geplanten Aufgabe. Funktion, Angebot, Admin und Empfängerauswahl bleiben unveränderlich. Wenn after_date_run im Update enthalten ist, aktiviert der Legacy-Hook diese Eigenschaft für eigene E-Mail-Vorlagen unabhängig vom übergebenen Wahrheitswert; eingebaute mail/financ/job-Funktionen lehnen dies ab. Die öffentliche ID im Pfad wählt das Objekt; ein zusätzliches id im Umschlag hat beim Legacy-Normalisierer Vorrang. - Operation-ID: put_v2_task_id - Dokumentation: /docs/api/v2/operations/put-v2-task-id-f7b20cee86cd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "task_name": "Erinnerung am Vorabend", "date_run": "2026-11-01T18:00:00+01:00" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/task/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "task_name": "Erinnerung am Vorabend", "date_run": "2026-11-01T18:00:00+01:00" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"task_name\":\"Erinnerung am Vorabend\",\"date_run\":\"2026-11-01T18:00:00+01:00\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_task_id","summary":"Geplante aufgabe ändern","description":"Ändert Name, Termin oder nachträgliche Ausführung einer geplanten Aufgabe. Funktion, Angebot, Admin und Empfängerauswahl bleiben unveränderlich. Wenn after_date_run im Update enthalten ist, aktiviert der Legacy-Hook diese Eigenschaft für eigene E-Mail-Vorlagen unabhängig vom übergebenen Wahrheitswert; eingebaute mail/financ/job-Funktionen lehnen dies ab. Die öffentliche ID im Pfad wählt das Objekt; ein zusätzliches id im Umschlag hat beim Legacy-Normalisierer Vorrang.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, update das tatsächliche Änderungsergebnis; update=false ist keine Bestätigung einer Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"task_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^task_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"task_name":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 250 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Erinnerung senden"]},"date_run":{"type":["string","number","boolean","null"],"description":"Datum/Zeit als durch PHP strtotime lesbarer String empfohlen (ISO 8601). Ungültige Werte ergeben intern 0; bei Erstellung wird mindestens jetzt + 180 Sekunden gesetzt."},"after_date_run":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[false]}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Geplante aufgabe ändern","value":{"data":{"task_name":"Erinnerung am Vorabend","date_run":"2026-11-01T18:00:00+01:00"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"updateTask","line":359},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/task/list Geplante Aufgaben auflisten Liest noch nicht gelöschte geplante Aufgaben mit Funktion, Empfängerauswahl und Ausführungsstatus; zeigt auch bereits ausgeführte Aufgaben, solange sie nicht gelöscht wurden. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_task_list - Dokumentation: /docs/api/v2/operations/get-v2-task-list-0b749460ae73/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/task/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_task_list","summary":"Geplante Aufgaben auflisten","description":"Liest noch nicht gelöschte geplante Aufgaben mit Funktion, Empfängerauswahl und Ausführungsstatus; zeigt auch bereits ausgeführte Aufgaben, solange sie nicht gelöscht wurden. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"task_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"task_012345abcdef_123456":{"id":"task_012345abcdef_123456","offer":"offer_012345abcdef_123456","admin":"admin_012345abcdef_123456","task_name":"Erinnerung senden","task_func":{"func":"mail","id":1},"date_run":"2026-11-01T09:00:00+01:00","status":"pending","user_all":true}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["task_012345abcdef_123456"]},"offer":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":107,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["offer_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"admin":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":121,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["admin_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"note":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":135,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["note_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"task_func":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"type":"string","enum":["mail","job","financ"]},"id":{"type":"integer"}},"additionalProperties":true,"required":["func","id"],"not":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"x-edoobox-id-kind":"builtin-task-function","description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported."}},"additionalProperties":true,"required":["func","id"]}]},"description":"Legacy stored task strings can serialize an out-of-range numeric selector because the response setter does not revalidate TaskFunction. No builtin-selector exception applies to this unreviewed fallback.","x-edoobox-schemaStatus":"legacy-unverified-output"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":24,"reason":"Defines built-in mail/job/financ selector numbers.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":149,"reason":"Input is a {func,id} object, with a public ID only for emailtemplate tasks.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":164,"reason":"Response returns a {func,id} object or false.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":195,"reason":"Generic PUT does not update task_func.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"task_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":197,"type":"string"},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":210,"lineStart":210,"lineEnd":220,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"1f7b1a142db47f2dffeebf0a8d1172504d8c2ddc9bc7a4d700e50f210f123794","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"lineStart":11629,"lineEnd":11639,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"a6fb9b218b0bdd5d6bc05d62034b2a1096d3279599319f181e03bc4a6fe7d83a","reason":"dQ returns ISO date-time or false from stored time."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_run":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":221,"anyOf":[{"type":"string"},{"const":false}]},"date_done":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":234,"lineStart":234,"lineEnd":243,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"0227cf29a76758114e5befab84b26299e35459dd4525be8100a9b7c3c3599f00","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"status":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":244,"lineStart":244,"lineEnd":254,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"d44e6abc76bc948fc683f708a06509f13f78708f49d98c9e864920973bbedce0","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/edooboxhelp_helper.php","line":11560,"lineStart":11560,"lineEnd":11577,"sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e","spanSha256":"47b45db25b5ed86673fe43c17c7d716427b0d86011b1ba918d19eebc39907fd1","reason":"aP text normalization and truncation. Reviewed aP update preserves legacy encoded storage and aQ read semantics. Truncation removes a dangling final escape backslash rather than creating a broken SQL literal; it remains normalization, not a rejecting raw input maxLength."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_start":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":255,"lineStart":255,"lineEnd":261,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"dff6d4e0c8a68a28ed937d50a9a8f2863ecdde7498ceb2f9f7d594c66d89079d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"date_finish":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":262,"lineStart":262,"lineEnd":268,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"6c5a09900705a96e2fe91f569454abe9a589459b01c89a44a644bb7cea8c3d30","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"user":{"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":269,"reason":"POST consumes an array of public user IDs and stores decoded IDs internally.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":281,"reason":"Response maps stored numeric values to public user-ID array.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"},{"file":"application/models/v2/Task.php","line":290,"reason":"Generic PUT does not update this field.","sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3"}]},"user_all":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":292,"type":"boolean"},"after_date_run":{"type":"boolean","x-edoobox-contractSource":[{"file":"application/models/v2/Task.php","line":302,"lineStart":302,"lineEnd":311,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"782961cfc0bedc92b0a42209c7c84f56a4643848b0d96f936bc628f8eadbc165","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Taskfunc.php","line":170,"lineStart":170,"lineEnd":213,"sha256":"e20d3b884a2cf83f0b8a95f1e19534e684ce741fde97eda5182b8e97d1944848","spanSha256":"b546ec9ee247cc9c06af332c90758a57ba79749cb211d3e4439634ed35a8c18c","reason":"func delegates to _checkUpdateTaskToNew"},{"file":"application/models/v2/Task.php","line":342,"lineStart":342,"lineEnd":366,"sha256":"36a9fb5dbb396f3fe3222d266d82062c5a875f1854e818b4ff8d8f3825cd13b3","spanSha256":"88f3b7a4db888adaec6d197892b2c902796086d8e93a18cc6a4e2f412897aa95","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"getTask","line":393},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/task/list/LUT Verfügbare Aufgabenfunktionen lesen Liefert den eingebauten Katalog für task_func: Erinnerungs-, Bestätigungs-, Freigabe-, Absage- und Zertifikatsmails, Sammeldatei sowie Zahlungs-/Rechnungsaufgaben. Die kleinen numerischen id-Werte sind Funktionscodes aus diesem Katalog, keine Datenbank-IDs. Eigene emailtemplate-Funktionen werden in diesem Katalog nicht aufgezählt. - Operation-ID: get_v2_task_list_LUT - Dokumentation: /docs/api/v2/operations/get-v2-task-list-lut-2665b2db9f99/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/task/list/LUT' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/task/list/LUT"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/task/list/LUT" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_task_list_LUT","summary":"Verfügbare Aufgabenfunktionen lesen","description":"Liefert den eingebauten Katalog für task_func: Erinnerungs-, Bestätigungs-, Freigabe-, Absage- und Zertifikatsmails, Sammeldatei sowie Zahlungs-/Rechnungsaufgaben. Die kleinen numerischen id-Werte sind Funktionscodes aus diesem Katalog, keine Datenbank-IDs. Eigene emailtemplate-Funktionen werden in diesem Katalog nicht aufgezählt.","tags":["task"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Liste von Übersetzungscode und task_func-Objekt.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"text_code":"N12128","data":{"func":"mail","id":1}}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"text_code":{"type":"string"},"data":{"anyOf":[{"type":"object","properties":{"func":{"const":"mail"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3,4,5]},{"type":"string","enum":["1","2","3","4","5"]}],"description":"Source-defined mail function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"job"},"id":{"anyOf":[{"type":"integer","enum":[1]},{"type":"string","enum":["1"]}],"description":"Source-defined job function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"financ"},"id":{"anyOf":[{"type":"integer","enum":[1,2,3]},{"type":"string","enum":["1","2","3"]}],"description":"Source-defined financ function selector, not a database record ID; only the paired func-specific enum values are supported. Funktionscode des gewählten Aufgabentyps; keine Datenbank-ID.","x-edoobox-id-kind":"builtin-task-function"}},"additionalProperties":true,"required":["func","id"]},{"type":"object","properties":{"func":{"const":"emailtemplate"},"id":{"type":"string","pattern":"^emailtemplate_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche emailtemplate_-ID einer verwendbaren E-Mail-Vorlagenfamilie desselben Kontos. Diese Ausnahme verwendet eine v2-ID statt eines eingebauten numerischen Funktionscodes; PDF-Vorlagen sind unzulässig.","examples":["emailtemplate_012345abcdef_123456"]}},"additionalProperties":true,"required":["func","id"]}],"description":"Built-in task IDs are function selectors, not database record IDs. Email-template tasks require a public emailtemplate ID for a usable mail template."}},"additionalProperties":true,"required":["text_code","data"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Adminconfig.php","line":154},"reason":"Local Structure selection and GET_data, or literal task lookup values; no domain writes."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Task.php","method":"getTask","line":393,"branch":{"file":"application/models/v2/Task.php","line":398}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"task","short":"task","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/template Buchungsansicht für einen Website-Pfad rendern Rendert die öffentliche Angebotsübersicht, Angebotsdetailseite oder Zertifikatsansicht im wirksamen Buchungsdesign. filter mit property=template.path bestimmt den Website-Pfad; ohne Pfad wird die Startübersicht geladen. Optional setzt provider.url die Basisadresse für Links. edlink ersetzt einen template.path-Filter nur bei auto=true. no_jsonld=true oder 1 unterdrückt JSON-LD; die Antwort enthält header, content und footer, einschließlich HTML in content.widgets. Inhalte, Widgetoptionen und optionale Administrationsdaten hängen vom Konto, Pfad und angemeldeten Benutzer ab. Es werden keine Buchungen durch diese Lesefunktion angelegt; Renderdaten können im Cache gespeichert werden. - Operation-ID: get_v2_template - Dokumentation: /docs/api/v2/operations/get-v2-template-bc05787cf104/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | Website-Pfad und optional provider.url als Basisadresse. Der Pfad enthält öffentliche URL-Segmente, keine rohen Datenbank-IDs. | | no_jsonld | query | nein | | | edlink | query | nein | Optionaler Ersatzpfad, wenn template.path den Eintrag auto=true enthält. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"Website-Pfad und optional provider.url als Basisadresse. Der Pfad enthält öffentliche URL-Segmente, keine rohen Datenbank-IDs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"auto":{"type":"boolean"}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"template.path","value":"de/kurse","auto":true},{"property":"provider.url","value":"https://www.example.invalid/kurse"}]}}},{"name":"no_jsonld","in":"query","required":false,"description":"","schema":{"type":["boolean","integer","string"],"examples":["true"],"description":"true, 1 sowie die Zeichenfolgen true/1 aktivieren die Option; andere Werte deaktivieren sie."}},{"name":"edlink","in":"query","required":false,"description":"Optionaler Ersatzpfad, wenn template.path den Eintrag auto=true enthält.","schema":{"type":"string","examples":["de/kurse"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/template' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/template"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/template" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_template","summary":"Buchungsansicht für einen Website-Pfad rendern","description":"Rendert die öffentliche Angebotsübersicht, Angebotsdetailseite oder Zertifikatsansicht im wirksamen Buchungsdesign. filter mit property=template.path bestimmt den Website-Pfad; ohne Pfad wird die Startübersicht geladen. Optional setzt provider.url die Basisadresse für Links. edlink ersetzt einen template.path-Filter nur bei auto=true. no_jsonld=true oder 1 unterdrückt JSON-LD; die Antwort enthält header, content und footer, einschließlich HTML in content.widgets. Inhalte, Widgetoptionen und optionale Administrationsdaten hängen vom Konto, Pfad und angemeldeten Benutzer ab. Es werden keine Buchungen durch diese Lesefunktion angelegt; Renderdaten können im Cache gespeichert werden.","tags":["template"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"Website-Pfad und optional provider.url als Basisadresse. Der Pfad enthält öffentliche URL-Segmente, keine rohen Datenbank-IDs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"auto":{"type":"boolean"}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"template.path","value":"de/kurse","auto":true},{"property":"provider.url","value":"https://www.example.invalid/kurse"}]}}},{"name":"no_jsonld","in":"query","required":false,"description":"","schema":{"type":["boolean","integer","string"],"examples":["true"],"description":"true, 1 sowie die Zeichenfolgen true/1 aktivieren die Option; andere Werte deaktivieren sie."}},{"name":"edlink","in":"query","required":false,"description":"Optionaler Ersatzpfad, wenn template.path den Eintrag auto=true enthält.","schema":{"type":"string","examples":["de/kurse"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält die dynamischen Objekte header, content und footer. content.widgets ist HTML; keine reine HTML- oder Binärantwort.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"header":{"title":"Kurse","bootstrap_version":5.3},"content":{"widgets":"
Beispielkurs
","widgets_list":["textb"],"admin_menu":false},"footer":{"gtm_id":""}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"header":{"type":"object","additionalProperties":true,"description":"Dynamische Seitenmetadaten, CSS- und Bootstrap-Informationen."},"content":{"type":"object","properties":{"widgets":{"type":"string","description":"Gerenderte HTML-Fragmente der sichtbaren Widgets."},"widgets_list":{"type":"array","items":{"type":"string"}},"admin_menu":{"description":"false oder optionale, berechtigungsabhängige Administrationsdaten."}},"additionalProperties":true,"required":["widgets","widgets_list","admin_menu"]},"footer":{"type":"object","additionalProperties":true}},"additionalProperties":true,"required":["header","content","footer"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Template.php","method":"getTemplate","line":80},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"template","short":"teml","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/text Übersetzungstexte auflisten Liest kontoeigene Sprachfassungen von Angebotsnamen, Beschreibungen, Zusatztexten, Preis- und Fragebezeichnungen. resource und resource_id ordnen den Text dem fachlichen Objekt zu. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_text - Dokumentation: /docs/api/v2/operations/get-v2-text-e76570e74051/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/text' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/text"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/text" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_text","summary":"Übersetzungstexte auflisten","description":"Liest kontoeigene Sprachfassungen von Angebotsnamen, Beschreibungen, Zusatztexten, Preis- und Fragebezeichnungen. resource und resource_id ordnen den Text dem fachlichen Objekt zu. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["text"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"text_012345abcdef_123456":{"id":"text_012345abcdef_123456","value":"Einführungskurs","language":"de","resource":"offer","resource_id":"offer_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Text.php","method":"getText","line":403},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"text","short":"text","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/text Übersetzungstext anlegen Legt eine bisher nicht vorhandene Kombination aus Sprache, resource und resource_id an. Die Sprache muss unterstützt sein, das Zielobjekt muss dem Konto gehören. Angebots-/Kategorienamen werden auf 100 Zeichen begrenzt; Kategorienamen zusätzlich bereinigt. Beschreibungen aktualisieren Kurztexte, Namen aktualisieren die Angebots-URL. data erlaubt ein einzelnes Objekt oder höchstens 100 Objekte als Batch; die Antwort enthält eine ID bzw. eine ID-Liste. - Operation-ID: post_v2_text - Dokumentation: /docs/api/v2/operations/post-v2-text-aec1920c03c8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "value": "Einführungskurs", "language": "de", "resource": "offer", "resource_id": "offer_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/text' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/text"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "value": "Einführungskurs", "language": "de", "resource": "offer", "resource_id": "offer_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/text" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"value\":\"Einführungskurs\",\"language\":\"de\",\"resource\":\"offer\",\"resource_id\":\"offer_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_text","summary":"Übersetzungstext anlegen","description":"Legt eine bisher nicht vorhandene Kombination aus Sprache, resource und resource_id an. Die Sprache muss unterstützt sein, das Zielobjekt muss dem Konto gehören. Angebots-/Kategorienamen werden auf 100 Zeichen begrenzt; Kategorienamen zusätzlich bereinigt. Beschreibungen aktualisieren Kurztexte, Namen aktualisieren die Angebots-URL. data erlaubt ein einzelnes Objekt oder höchstens 100 Objekte als Batch; die Antwort enthält eine ID bzw. eine ID-Liste.","tags":["text"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue öffentliche ID oder beim Batch eine Liste; insert zeigt das Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"text_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^text_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^text_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"value":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 50000 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Einführungskurs"]},"language":{"type":"string"},"resource":{"type":"string","enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["value","language","resource","resource_id"]},{"type":"array","items":{"type":"object","properties":{"value":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 50000 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Einführungskurs"]},"language":{"type":"string"},"resource":{"type":"string","enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["value","language","resource","resource_id"]},"minItems":1,"maxItems":100}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Übersetzungstext anlegen","value":{"data":{"value":"Einführungskurs","language":"de","resource":"offer","resource_id":"offer_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Text.php","method":"postText","line":342},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"text","short":"text","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/text Übersetzungstext ändern Ändert den Textwert einer vorhandenen Sprachfassung. Sprache, Ressourcentyp und Zielobjekt bleiben unverändert. Namen der Root-, Archiv- und Papierkorb-Kategorien sind gesperrt; Beschreibungsänderungen aktualisieren Kurztexte, Namensänderungen die URL. id im JSON-Umschlag ist Pflicht. data.resource darf als unveränderlicher Validierungskontext mitgesendet werden und wird insbesondere für die Leerprüfung von Angebots-/Kategorienamen gelesen; es ändert weder Typ noch Verknüpfung. data.language und data.resource_id sind keine schreibbaren Updatefelder. - Operation-ID: put_v2_text - Dokumentation: /docs/api/v2/operations/put-v2-text-03a765c66122/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "value": "Aktualisierter Einführungskurs", "resource": "offer" }, "id": "text_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/text' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/text"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "value": "Aktualisierter Einführungskurs", "resource": "offer" }, "id": "text_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/text" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"value\":\"Aktualisierter Einführungskurs\",\"resource\":\"offer\"},\"id\":\"text_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_text","summary":"Übersetzungstext ändern","description":"Ändert den Textwert einer vorhandenen Sprachfassung. Sprache, Ressourcentyp und Zielobjekt bleiben unverändert. Namen der Root-, Archiv- und Papierkorb-Kategorien sind gesperrt; Beschreibungsänderungen aktualisieren Kurztexte, Namensänderungen die URL. id im JSON-Umschlag ist Pflicht. data.resource darf als unveränderlicher Validierungskontext mitgesendet werden und wird insbesondere für die Leerprüfung von Angebots-/Kategorienamen gelesen; es ändert weder Typ noch Verknüpfung. data.language und data.resource_id sind keine schreibbaren Updatefelder.","tags":["text"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, update das tatsächliche Änderungsergebnis; update=false ist keine Bestätigung einer Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"text_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^text_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"value":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 50000 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Einführungskurs"]},"resource":{"x-edoobox-update-context":true,"x-edoobox-schemaStatus":"source-reviewed-hook-context","description":"Immutable resource selector retained as update validation context: Textfunc::_updateText reads the original data.resource for offer/category name checks. This hook does not invoke the Structure resource getter, so its create-time selector enum is not a rejecting PUT wire constraint. The generic writer does not change persisted text_type; other input is not interpreted as a new resource selection.\n\nUnveränderlicher Ressourcentyp als Validierungskontext: den tatsächlichen Typ der vorhandenen Sprachfassung mitsenden, insbesondere offer/category für die Prüfung nichtleerer Namen. Der Hook liest diesen Wert unmittelbar, ohne den Erstellungs-Enum zu validieren; andere Werte wählen keine neue Ressource und verändern den gespeicherten text_type nicht.","examples":["description"],"x-edoobox-knownInputs":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"language":false,"resource_id":false},"additionalProperties":true},"id":{"type":"string","pattern":"^text_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Übersetzungstext ändern","value":{"data":{"value":"Aktualisierter Einführungskurs","resource":"offer"},"id":"text_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Text.php","method":"updateText","line":360},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"text","short":"text","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/text/{id} Übersetzungstext lesen Liest kontoeigene Sprachfassungen von Angebotsnamen, Beschreibungen, Zusatztexten, Preis- und Fragebezeichnungen. resource und resource_id ordnen den Text dem fachlichen Objekt zu. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_text_id - Dokumentation: /docs/api/v2/operations/get-v2-text-id-805b5a77060c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/text/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/text/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/text/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_text_id","summary":"Übersetzungstext lesen","description":"Liest kontoeigene Sprachfassungen von Angebotsnamen, Beschreibungen, Zusatztexten, Preis- und Fragebezeichnungen. resource und resource_id ordnen den Text dem fachlichen Objekt zu. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["text"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"text_012345abcdef_123456":{"id":"text_012345abcdef_123456","value":"Einführungskurs","language":"de","resource":"offer","resource_id":"offer_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Text.php","method":"getText","line":403},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"text","short":"text","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/text/{id} Übersetzungstext ändern Ändert den Textwert einer vorhandenen Sprachfassung. Sprache, Ressourcentyp und Zielobjekt bleiben unverändert. Namen der Root-, Archiv- und Papierkorb-Kategorien sind gesperrt; Beschreibungsänderungen aktualisieren Kurztexte, Namensänderungen die URL. Die öffentliche ID im Pfad wählt das Objekt; ein zusätzliches id im Umschlag hat beim Legacy-Normalisierer Vorrang. data.resource darf als unveränderlicher Validierungskontext mitgesendet werden und wird insbesondere für die Leerprüfung von Angebots-/Kategorienamen gelesen; es ändert weder Typ noch Verknüpfung. data.language und data.resource_id sind keine schreibbaren Updatefelder. - Operation-ID: put_v2_text_id - Dokumentation: /docs/api/v2/operations/put-v2-text-id-711d3f62eaff/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "value": "Aktualisierter Einführungskurs", "resource": "offer" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/text/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/text/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "value": "Aktualisierter Einführungskurs", "resource": "offer" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/text/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"value\":\"Aktualisierter Einführungskurs\",\"resource\":\"offer\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_text_id","summary":"Übersetzungstext ändern","description":"Ändert den Textwert einer vorhandenen Sprachfassung. Sprache, Ressourcentyp und Zielobjekt bleiben unverändert. Namen der Root-, Archiv- und Papierkorb-Kategorien sind gesperrt; Beschreibungsänderungen aktualisieren Kurztexte, Namensänderungen die URL. Die öffentliche ID im Pfad wählt das Objekt; ein zusätzliches id im Umschlag hat beim Legacy-Normalisierer Vorrang. data.resource darf als unveränderlicher Validierungskontext mitgesendet werden und wird insbesondere für die Leerprüfung von Angebots-/Kategorienamen gelesen; es ändert weder Typ noch Verknüpfung. data.language und data.resource_id sind keine schreibbaren Updatefelder.","tags":["text"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, update das tatsächliche Änderungsergebnis; update=false ist keine Bestätigung einer Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"text_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^text_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"value":{"type":["string","number","boolean","null","array","object"],"description":"Text als String; Leerraum und Zeilenumbrüche werden normalisiert und auf 50000 Zeichen gekürzt. Andere JSON-Typen werden vom Legacy-Setter zu leerem Text normalisiert.","examples":["Einführungskurs"]},"resource":{"x-edoobox-update-context":true,"x-edoobox-schemaStatus":"source-reviewed-hook-context","description":"Immutable resource selector retained as update validation context: Textfunc::_updateText reads the original data.resource for offer/category name checks. This hook does not invoke the Structure resource getter, so its create-time selector enum is not a rejecting PUT wire constraint. The generic writer does not change persisted text_type; other input is not interpreted as a new resource selection.\n\nUnveränderlicher Ressourcentyp als Validierungskontext: den tatsächlichen Typ der vorhandenen Sprachfassung mitsenden, insbesondere offer/category für die Prüfung nichtleerer Namen. Der Hook liest diesen Wert unmittelbar, ohne den Erstellungs-Enum zu validieren; andere Werte wählen keine neue Ressource und verändern den gespeicherten text_type nicht.","examples":["description"],"x-edoobox-knownInputs":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"language":false,"resource_id":false},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Übersetzungstext ändern","value":{"data":{"value":"Aktualisierter Einführungskurs","resource":"offer"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Text.php","method":"updateText","line":360},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"text","short":"text","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/text/list Übersetzungstexte auflisten Liest kontoeigene Sprachfassungen von Angebotsnamen, Beschreibungen, Zusatztexten, Preis- und Fragebezeichnungen. resource und resource_id ordnen den Text dem fachlichen Objekt zu. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_text_list - Dokumentation: /docs/api/v2/operations/get-v2-text-list-3e39e93fd92b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/text/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/text/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/text/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_text_list","summary":"Übersetzungstexte auflisten","description":"Liest kontoeigene Sprachfassungen von Angebotsnamen, Beschreibungen, Zusatztexten, Preis- und Fragebezeichnungen. resource und resource_id ordnen den Text dem fachlichen Objekt zu. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["text"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"text_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"text_012345abcdef_123456":{"id":"text_012345abcdef_123456","value":"Einführungskurs","language":"de","resource":"offer","resource_id":"offer_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["text_012345abcdef_123456"]},"value":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":39,"type":"string"},"language":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Text.php","line":52,"lineStart":52,"lineEnd":70,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"ccbc0f002f2d86440b76ef8a1c6ac8f3d2472734846f3f40bf92e06470d5650a","reason":"Structure getter, output setter and field flags."},{"file":"application/helpers/langedoobox_helper.php","line":534,"lineStart":534,"lineEnd":537,"sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c","spanSha256":"c125ef0d3f82a8eda624e88bb9813ddf5fbe865b844f1e4a794f575e76842e9e","reason":"Language config lookup."},{"file":"application/config/config.php","line":93,"lineStart":93,"lineEnd":220,"sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82","spanSha256":"efce9b0b1d55ad7ef4e515717cec33fc9327a20b66445750062a6e7a15421561","reason":"Exact language keys."},{"file":"application/models/v2/Textfunc.php","line":5,"lineStart":5,"lineEnd":58,"sha256":"48e7edda3f585fdaabd91a648ec23962d8ec843d4db954586f38cad816e2942a","spanSha256":"83a4da705fb6e7c6beee950d3df4272222068baa26e4de59757c63ec130ec16f","reason":"Before-insert language/resource duplicate and text check."},{"file":"application/models/v2/Text.php","line":342,"lineStart":342,"lineEnd":349,"sha256":"efe37122c63dfec7beaa4d149fa5fad18106f8d5144e939206db9f8fd6d1793a","spanSha256":"c3339244a8ff942ee96b6daa13ece5d0a2e4b9f35d45bf0959a4189da1fbfc0f","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"resource":{"enum":["category","offer","pricecategory","description","admin_description","url","additionaltext1","additionaltext2","additionaltext3","deliverablesgroup_name","deliverablesgroup_description","deliverables_name","deliverables_description","questiongroup_name","questiongroup_description","question_name","question_description"]},"resource_id":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":212,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["resourceid_012345abcdef_123456"]},{"const":false},{"type":"null"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Text.php","method":"getText","line":403},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"text","short":"text","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/transaction Teilnehmerkontobuchungen anzeigen Liest Belastungen, Gutschriften und Zahlungen im Teilnehmerkonto mit öffentlichen Verknüpfungen. Ergänzt minimale userdata und gegebenenfalls balance_allocation mit Zielbuchung, Gegenposten und Quelle. Unterstützt Feldselektion, Filter, Sortierung, Pagination und response_type; auch die ID-Selektion ist standardmäßig eine Sammlung. - Operation-ID: get_v2_transaction - Dokumentation: /docs/api/v2/operations/get-v2-transaction-cd253bcd2fc6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/transaction' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_transaction","summary":"Teilnehmerkontobuchungen anzeigen","description":"Liest Belastungen, Gutschriften und Zahlungen im Teilnehmerkonto mit öffentlichen Verknüpfungen. Ergänzt minimale userdata und gegebenenfalls balance_allocation mit Zielbuchung, Gegenposten und Quelle. Unterstützt Feldselektion, Filter, Sortierung, Pagination und response_type; auch die ID-Selektion ist standardmäßig eine Sammlung.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Teilnehmerkontobuchungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"transaction_012345abcdef_123456":{"id":"transaction_012345abcdef_123456","number":123456,"amount":-120,"currency":"CHF","description":"Beispielkurs","user":"user_012345abcdef_123456","booking":false,"offer":false,"pay":false,"date_accounting":"2026-10-15T10:00:00+02:00","type":"t_manualtrans","canceled":false,"mustpay":false,"waiting_list":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"number":{"type":"integer"},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["offer_012345abcdef_123456"]},{"const":false}]},"pay":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},{"const":false}]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"userdata":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true},"balance_allocation":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"counterpart_transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"payment_transaction":{"anyOf":[{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},{"const":false}]},"release_reason":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"number":{"type":"integer"},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["offer_012345abcdef_123456"]},{"const":false}]},"pay":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},{"const":false}]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"userdata":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true},"balance_allocation":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"counterpart_transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"payment_transaction":{"anyOf":[{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},{"const":false}]},"release_reason":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"getTransaction","line":439},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/transaction Manuelle Teilnehmerkontobuchung anlegen Legt eine manuelle Belastung oder Gutschrift für einen Teilnehmer an. Erforderlich sind amount, currency, description, user, date_accounting und type=t_manualtrans; andere Buchungstypen werden durch den Insert-Hook abgelehnt. vat ist optional und muss auf einen Steuersatz desselben Kontos verweisen. Vergibt eine neue Buchungsnummer und sperrt den Teilnehmersaldo; keine Onlinezahlung wird ausgelöst. Eine Liste mit bis zu 100 Datensätzen wird als Batch verarbeitet. - Operation-ID: post_v2_transaction - Dokumentation: /docs/api/v2/operations/post-v2-transaction-cda870a3b96a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "type": "t_manualtrans", "amount": -120, "currency": "CHF", "description": "Beispielkurs", "user": "user_012345abcdef_123456", "date_accounting": "2026-10-15T10:00:00+02:00", "vat": "vat_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/transaction' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "type": "t_manualtrans", "amount": -120, "currency": "CHF", "description": "Beispielkurs", "user": "user_012345abcdef_123456", "date_accounting": "2026-10-15T10:00:00+02:00", "vat": "vat_012345abcdef_123456" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"type\":\"t_manualtrans\",\"amount\":-120,\"currency\":\"CHF\",\"description\":\"Beispielkurs\",\"user\":\"user_012345abcdef_123456\",\"date_accounting\":\"2026-10-15T10:00:00+02:00\",\"vat\":\"vat_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_transaction","summary":"Manuelle Teilnehmerkontobuchung anlegen","description":"Legt eine manuelle Belastung oder Gutschrift für einen Teilnehmer an. Erforderlich sind amount, currency, description, user, date_accounting und type=t_manualtrans; andere Buchungstypen werden durch den Insert-Hook abgelehnt. vat ist optional und muss auf einen Steuersatz desselben Kontos verweisen. Vergibt eine neue Buchungsnummer und sperrt den Teilnehmersaldo; keine Onlinezahlung wird ausgelöst. Eine Liste mit bis zu 100 Datensätzen wird als Batch verarbeitet.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"insert=true und öffentliche transaction-ID, beim Batch eine ID-Liste.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"transaction_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]}}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"amount":{"description":"Zahl oder numerischer Wert als Text: auf zwei Stellen gerundet; nichtnumerische Eingaben normalisieren zu 0. Negativ = Belastung, positiv = Gutschrift.","x-edoobox-canonicalType":"number","examples":[-120,120]},"currency":{"description":"Währungszeichenfolge, beim Speichern auf drei Zeichen normalisiert.","x-edoobox-canonicalType":"string","examples":["CHF"]},"description":{"description":"Beschreibung als Text; aP normalisiert und kürzt den gespeicherten Text.","x-edoobox-canonicalType":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"date_accounting":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"type":{"const":"t_manualtrans"},"vat":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]}},"additionalProperties":true,"required":["amount","currency","description","user","date_accounting","type"]},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"amount":{"description":"Zahl oder numerischer Wert als Text: auf zwei Stellen gerundet; nichtnumerische Eingaben normalisieren zu 0. Negativ = Belastung, positiv = Gutschrift.","x-edoobox-canonicalType":"number","examples":[-120,120]},"currency":{"description":"Währungszeichenfolge, beim Speichern auf drei Zeichen normalisiert.","x-edoobox-canonicalType":"string","examples":["CHF"]},"description":{"description":"Beschreibung als Text; aP normalisiert und kürzt den gespeicherten Text.","x-edoobox-canonicalType":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"date_accounting":{"type":"string","description":"Mit PHP strtotime parsebarer Datumstext; kanonisch ISO 8601 mit Zeitzone.","examples":["2026-10-15T10:00:00+02:00"]},"type":{"const":"t_manualtrans"},"vat":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]}},"additionalProperties":true,"required":["amount","currency","description","user","date_accounting","type"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Manuelle Teilnehmerkontobuchung anlegen","value":{"data":{"type":"t_manualtrans","amount":-120,"currency":"CHF","description":"Beispielkurs","user":"user_012345abcdef_123456","date_accounting":"2026-10-15T10:00:00+02:00","vat":"vat_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"postTransaction","line":349},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/transaction/{id} Teilnehmerkontobuchung prüfen Liest Belastungen, Gutschriften und Zahlungen im Teilnehmerkonto mit öffentlichen Verknüpfungen. Ergänzt minimale userdata und gegebenenfalls balance_allocation mit Zielbuchung, Gegenposten und Quelle. Unterstützt Feldselektion, Filter, Sortierung, Pagination und response_type; auch die ID-Selektion ist standardmäßig eine Sammlung. - Operation-ID: get_v2_transaction_id - Dokumentation: /docs/api/v2/operations/get-v2-transaction-id-c6639ed86c0c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["transaction_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/transaction/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_transaction_id","summary":"Teilnehmerkontobuchung prüfen","description":"Liest Belastungen, Gutschriften und Zahlungen im Teilnehmerkonto mit öffentlichen Verknüpfungen. Ergänzt minimale userdata und gegebenenfalls balance_allocation mit Zielbuchung, Gegenposten und Quelle. Unterstützt Feldselektion, Filter, Sortierung, Pagination und response_type; auch die ID-Selektion ist standardmäßig eine Sammlung.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["transaction_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Teilnehmerkontobuchungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"transaction_012345abcdef_123456":{"id":"transaction_012345abcdef_123456","number":123456,"amount":-120,"currency":"CHF","description":"Beispielkurs","user":"user_012345abcdef_123456","booking":false,"offer":false,"pay":false,"date_accounting":"2026-10-15T10:00:00+02:00","type":"t_manualtrans","canceled":false,"mustpay":false,"waiting_list":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"number":{"type":"integer"},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["offer_012345abcdef_123456"]},{"const":false}]},"pay":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},{"const":false}]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"userdata":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true},"balance_allocation":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"counterpart_transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"payment_transaction":{"anyOf":[{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},{"const":false}]},"release_reason":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"number":{"type":"integer"},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["offer_012345abcdef_123456"]},{"const":false}]},"pay":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},{"const":false}]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"userdata":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true},"balance_allocation":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"counterpart_transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"payment_transaction":{"anyOf":[{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},{"const":false}]},"release_reason":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"getTransaction","line":439},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/transaction/{id}/camt Unzugeordneten Bankeingang löschen Löscht den gewählten importierten CAMT-Bankeingang unwiderruflich. Nur noch unzugeordnete Eingänge sind löschbar; bereits zugewiesene Eingänge liefern already_assigned. Pfad-id ist eine öffentliche camt-ID. - Operation-ID: delete_v2_transaction_id_camt - Dokumentation: /docs/api/v2/operations/delete-v2-transaction-id-camt-51961630c4b2/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche ID des unzugeordneten Bankeingangs. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche ID des unzugeordneten Bankeingangs.","schema":{"type":"string","pattern":"^camt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["camt_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/transaction/'"${PATH_ID}"'/camt' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/{id}/camt"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction/{id}/camt" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_transaction_id_camt","summary":"Unzugeordneten Bankeingang löschen","description":"Löscht den gewählten importierten CAMT-Bankeingang unwiderruflich. Nur noch unzugeordnete Eingänge sind löschbar; bereits zugewiesene Eingänge liefern already_assigned. Pfad-id ist eine öffentliche camt-ID.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche ID des unzugeordneten Bankeingangs.","schema":{"type":"string","pattern":"^camt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["camt_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche camt-ID und delete=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"camt_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^camt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["camt_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":true},"examples":{"request":{"summary":"Unzugeordneten Bankeingang löschen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"deleteTransaction","line":417,"branch":{"file":"application/models/v2/Transaction.php","line":423}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/transaction/{id}/camt Bankeingang einer Rechnung zuordnen Ordnet einen bisher nicht zugewiesenen CAMT-Bankeingang einer eigenen Rechnung zu und verbucht dessen Betrag am Bankbuchungsdatum. Pfad-id ist eine camt-ID, data.invoice eine invoice-ID. Unterschiedliche Währungen oder bereits zugewiesene Eingänge werden abgelehnt. - Operation-ID: put_v2_transaction_id_camt - Dokumentation: /docs/api/v2/operations/put-v2-transaction-id-camt-5b6eb212b8c5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche ID des unzugeordneten Bankeingangs. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche ID des unzugeordneten Bankeingangs.","schema":{"type":"string","pattern":"^camt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["camt_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "invoice": "invoice_012345abcdef_123456" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/transaction/'"${PATH_ID}"'/camt' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/{id}/camt"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "invoice": "invoice_012345abcdef_123456" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction/{id}/camt" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"invoice\":\"invoice_012345abcdef_123456\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_transaction_id_camt","summary":"Bankeingang einer Rechnung zuordnen","description":"Ordnet einen bisher nicht zugewiesenen CAMT-Bankeingang einer eigenen Rechnung zu und verbucht dessen Betrag am Bankbuchungsdatum. Pfad-id ist eine camt-ID, data.invoice eine invoice-ID. Unterschiedliche Währungen oder bereits zugewiesene Eingänge werden abgelehnt.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche ID des unzugeordneten Bankeingangs.","schema":{"type":"string","pattern":"^camt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["camt_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"id/update und die öffentliche transaction-Kennung der verbuchten Zahlung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"camt_012345abcdef_123456","update":true,"transaction":"transaction_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^camt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["camt_012345abcdef_123456"]},"update":{"type":"boolean"},"transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"invoice":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"additionalProperties":true,"required":["invoice"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Bankeingang einer Rechnung zuordnen","value":{"data":{"invoice":"invoice_012345abcdef_123456"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"updateTransaction","line":393,"branch":{"file":"application/models/v2/Transaction.php","line":401}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/transaction/{id}/pay Zahlung und erstattbaren Restbetrag prüfen Liest eine verbuchte Zahlung aus dem eigenen Konto und berechnet erfolgreiche bisherige Erstattungen. Obwohl die Route transaction heißt, ist id hier die öffentliche pay-ID aus der Teilnehmerkontobuchung. Nur Saferpay, Stripe-Karte/SEPA und PayPal v2 werden als online erstattbar eingestuft. Der historische Schlüssel refound enthält repaid_amount/refundable_amount oder false. - Operation-ID: get_v2_transaction_id_pay - Dokumentation: /docs/api/v2/operations/get-v2-transaction-id-pay-957203885f46/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche pay-ID der ursprünglichen Zahlung; keine transaction-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche pay-ID der ursprünglichen Zahlung; keine transaction-ID.","schema":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/transaction/'"${PATH_ID}"'/pay' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/{id}/pay"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction/{id}/pay" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_transaction_id_pay","summary":"Zahlung und erstattbaren Restbetrag prüfen","description":"Liest eine verbuchte Zahlung aus dem eigenen Konto und berechnet erfolgreiche bisherige Erstattungen. Obwohl die Route transaction heißt, ist id hier die öffentliche pay-ID aus der Teilnehmerkontobuchung. Nur Saferpay, Stripe-Karte/SEPA und PayPal v2 werden als online erstattbar eingestuft. Der historische Schlüssel refound enthält repaid_amount/refundable_amount oder false.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche pay-ID der ursprünglichen Zahlung; keine transaction-ID.","schema":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Zahlung mit refundable und payment einschließlich historischem Feld refound.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"pay_012345abcdef_123456","paytrans":"paytrans_012345abcdef_123456","refundable":true,"payment":{"type":"p_stripe_c","token":"ch_example","currency":"CHF","amount":120,"refound":{"repaid_amount":20,"refundable_amount":100},"date":"2026-10-15T10:00:00+02:00","user":"user_012345abcdef_123456"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"refundable":{"type":"boolean"},"payment":{"type":"object","properties":{"type":{"type":"string"},"token":{"type":"string"},"currency":{"type":"string"},"amount":{"type":"number"},"refound":{"anyOf":[{"type":"object","properties":{"repaid_amount":{"type":"number"},"refundable_amount":{"type":"number"}},"additionalProperties":true},{"const":false}]},"date":{"anyOf":[{"type":"string"},{"const":false}]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/19","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Transaction.php","line":568,"reason":"Exact pay GET delegates _getTransactionPayData; _putTransactionRefund is selected separately by update handler."},"reason":"Read an existing payment and computed refundable amount; do not execute a charge or refund.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/19","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"getTransaction","line":439,"branch":{"file":"application/models/v2/Transaction.php","line":568}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/transaction/{id}/refund Onlinezahlung teilweise oder vollständig erstatten Erstattet einen positiven Betrag über Saferpay, Stripe oder PayPal v2 und legt anschließend Gegenbuchung sowie Erstattungseintrag an. Pfad-id bezeichnet die ursprüngliche pay-ID. amount muss nach Rundung positiv sein und darf den erstattbaren Rest nicht überschreiten; description muss nach Textnormalisierung länger als ein Zeichen sein. Erfolg aktualisiert betroffene Rechnungen; invoice_refresh_pending kann einen nachgelagerten Dokumentfehler melden, obwohl die Auszahlung bereits erfolgreich war. In diesem Fall die Erstattung keinesfalls zur Dokumentkorrektur wiederholen. - Operation-ID: put_v2_transaction_id_refund - Dokumentation: /docs/api/v2/operations/put-v2-transaction-id-refund-686ad3589b6d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche pay-ID der ursprünglichen Onlinezahlung. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche pay-ID der ursprünglichen Onlinezahlung.","schema":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "amount": 20, "description": "Teilnahme storniert" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/transaction/'"${PATH_ID}"'/refund' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/{id}/refund"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "amount": 20, "description": "Teilnahme storniert" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction/{id}/refund" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"amount\":20,\"description\":\"Teilnahme storniert\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_transaction_id_refund","summary":"Onlinezahlung teilweise oder vollständig erstatten","description":"Erstattet einen positiven Betrag über Saferpay, Stripe oder PayPal v2 und legt anschließend Gegenbuchung sowie Erstattungseintrag an. Pfad-id bezeichnet die ursprüngliche pay-ID. amount muss nach Rundung positiv sein und darf den erstattbaren Rest nicht überschreiten; description muss nach Textnormalisierung länger als ein Zeichen sein. Erfolg aktualisiert betroffene Rechnungen; invoice_refresh_pending kann einen nachgelagerten Dokumentfehler melden, obwohl die Auszahlung bereits erfolgreich war. In diesem Fall die Erstattung keinesfalls zur Dokumentkorrektur wiederholen.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche pay-ID der ursprünglichen Onlinezahlung.","schema":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erstattungsstatus, gegebenenfalls Fehlerdetails, betroffene Rechnungen und vor der Erstattung gelesene Zahlungsdetails. refund=true bestätigt die Auszahlung unabhängig vom Dokumentstatus.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"pay_012345abcdef_123456","update":true,"refund":true,"refund_error":false,"invoices":["invoice_012345abcdef_123456"],"invoice_refresh_pending":false,"pay":{"id":"pay_012345abcdef_123456","paytrans":"paytrans_012345abcdef_123456","refundable":true,"payment":{"type":"p_stripe_c","token":"ch_example","currency":"CHF","amount":120,"refound":{"repaid_amount":20,"refundable_amount":100},"date":"2026-10-15T10:00:00+02:00","user":"user_012345abcdef_123456"}}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},"update":{"type":"boolean"},"refund":{"type":"boolean"},"refund_error":{"type":"boolean"},"refund_error_code":{"type":["string","boolean"]},"refund_error_message":{"type":["string","boolean"]},"invoices":{"type":"array","items":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]}},"invoice_refresh_pending":{"type":"boolean"},"pay":{"type":"object","properties":{"id":{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},"paytrans":{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},"refundable":{"type":"boolean"},"payment":{"type":"object","properties":{"type":{"type":"string"},"token":{"type":"string"},"currency":{"type":"string"},"amount":{"type":"number"},"refound":{"anyOf":[{"type":"object","properties":{"repaid_amount":{"type":"number"},"refundable_amount":{"type":"number"}},"additionalProperties":true},{"const":false}]},"date":{"anyOf":[{"type":"string"},{"const":false}]},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"amount":{"anyOf":[{"type":"number"},{"type":"string","description":"Numerische Zeichenfolge; PHP is_numeric beziehungsweise die dokumentierte Zahlen-Normalisierung ist maßgeblich."}],"description":"Positiver Teil-/Gesamtbetrag in Zahlungswährung; Zahlen oder numerische Zeichenfolgen."},"description":{"type":"string","minLength":2,"description":"Grund der Erstattung; nach aP-Textnormalisierung mindestens zwei Zeichen."}},"additionalProperties":true,"required":["amount","description"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Onlinezahlung teilweise oder vollständig erstatten","value":{"data":{"amount":20,"description":"Teilnahme storniert"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"updateTransaction","line":393,"branch":{"file":"application/models/v2/Transaction.php","line":398}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/transaction/file/camt CAMT-Bankauszug importieren Importiert eine hochgeladene camt.053-XML-Datei und speichert neue Bankeingänge. Multipart-Feld camt ist erforderlich; Dateiendung .xml und Größe strikt unter 10.000.000 Bytes. Erkennt Dubletten anhand Zahlungsreferenzen und versucht passende Rechnungen automatisch zu bezahlen, wenn Währung und Voraussetzungen passen. Der Import ist eine Buchungsaktion; nach unklarem Ausgang nicht blind wiederholen. Die Route akzeptiert keinen Ersatz der Datei durch JSON-data. - Operation-ID: post_v2_transaction_file_camt - Dokumentation: /docs/api/v2/operations/post-v2-transaction-file-camt-fd16390e2620/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/transaction/file/camt' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/file/camt"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/transaction/file/camt" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_transaction_file_camt","summary":"CAMT-Bankauszug importieren","description":"Importiert eine hochgeladene camt.053-XML-Datei und speichert neue Bankeingänge. Multipart-Feld camt ist erforderlich; Dateiendung .xml und Größe strikt unter 10.000.000 Bytes. Erkennt Dubletten anhand Zahlungsreferenzen und versucht passende Rechnungen automatisch zu bezahlen, wenn Währung und Voraussetzungen passen. Der Import ist eine Buchungsaktion; nach unklarem Ausgang nicht blind wiederholen. Die Route akzeptiert keinen Ersatz der Datei durch JSON-data.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"id=false, Dublettenliste und template-Zähler Count1=neue Eingänge, Count2=automatische Zuordnungen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":false,"duplicate_entries":[],"template":{"text_code":"N53036","Count1":3,"Count2":2}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"const":false},"duplicate_entries":{"type":"array","items":{"type":"object","properties":{"additional_info":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"date_accounting":{"type":"string"},"payment_reference":{"type":"string"},"number":{"type":"string"}},"additionalProperties":true}},"template":{"type":"object","properties":{"text_code":{"const":"N53036"},"Count1":{"type":"integer"},"Count2":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"camt":{"type":"string","format":"binary","description":"camt.053-XML-Datei mit .xml-Endung, Größe <10.000.000 Bytes."}},"additionalProperties":true,"required":["camt"]},"encoding":{"camt":{"contentType":"application/xml"}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"postTransaction","line":172},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/transaction/list Teilnehmerkontobuchungen anzeigen Liest Belastungen, Gutschriften und Zahlungen im Teilnehmerkonto mit öffentlichen Verknüpfungen. Ergänzt minimale userdata und gegebenenfalls balance_allocation mit Zielbuchung, Gegenposten und Quelle. Unterstützt Feldselektion, Filter, Sortierung, Pagination und response_type; auch die ID-Selektion ist standardmäßig eine Sammlung. - Operation-ID: get_v2_transaction_list - Dokumentation: /docs/api/v2/operations/get-v2-transaction-list-52bfe786959f/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/transaction/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_transaction_list","summary":"Teilnehmerkontobuchungen anzeigen","description":"Liest Belastungen, Gutschriften und Zahlungen im Teilnehmerkonto mit öffentlichen Verknüpfungen. Ergänzt minimale userdata und gegebenenfalls balance_allocation mit Zielbuchung, Gegenposten und Quelle. Unterstützt Feldselektion, Filter, Sortierung, Pagination und response_type; auch die ID-Selektion ist standardmäßig eine Sammlung.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Teilnehmerkontobuchungen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"transaction_012345abcdef_123456":{"id":"transaction_012345abcdef_123456","number":123456,"amount":-120,"currency":"CHF","description":"Beispielkurs","user":"user_012345abcdef_123456","booking":false,"offer":false,"pay":false,"date_accounting":"2026-10-15T10:00:00+02:00","type":"t_manualtrans","canceled":false,"mustpay":false,"waiting_list":false}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"number":{"type":"integer"},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["offer_012345abcdef_123456"]},{"const":false}]},"pay":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},{"const":false}]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"userdata":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true},"balance_allocation":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"counterpart_transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"payment_transaction":{"anyOf":[{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},{"const":false}]},"release_reason":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"number":{"type":"integer"},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["offer_012345abcdef_123456"]},{"const":false}]},"pay":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},{"const":false}]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"userdata":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true},"balance_allocation":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"counterpart_transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"payment_transaction":{"anyOf":[{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},{"const":false}]},"release_reason":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"getTransaction","line":439},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/transaction/list/billable Abrechenbare Teilnehmerpositionen ermitteln Ermittelt für den im user-Filter gewählten Teilnehmer die derzeit neu abrechenbaren Kontopositionen. Ohne gültigen user-Filter wird required.user=false und ED80019 ausgegeben. Das Ergebnis kann direkt für die transaction-Liste zur Rechnungserstellung verwendet werden; allgemeine Listenparameter gelten für die Ausgabe. - Operation-ID: get_v2_transaction_list_billable - Dokumentation: /docs/api/v2/operations/get-v2-transaction-list-billable-04dcae8fe97b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | ja | Erforderlicher user-Filter mit öffentlicher Teilnehmer-ID. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"Erforderlicher user-Filter mit öffentlicher Teilnehmer-ID.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/transaction/list/billable' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/list/billable"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction/list/billable" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_transaction_list_billable","summary":"Abrechenbare Teilnehmerpositionen ermitteln","description":"Ermittelt für den im user-Filter gewählten Teilnehmer die derzeit neu abrechenbaren Kontopositionen. Ohne gültigen user-Filter wird required.user=false und ED80019 ausgegeben. Das Ergebnis kann direkt für die transaction-Liste zur Rechnungserstellung verwendet werden; allgemeine Listenparameter gelten für die Ausgabe.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"Erforderlicher user-Filter mit öffentlicher Teilnehmer-ID.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]},"minItems":1},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste abrechenbarer Teilnehmerkontobuchungen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"transaction_012345abcdef_123456":{"id":"transaction_012345abcdef_123456","number":123456,"amount":-120,"currency":"CHF","description":"Beispielkurs","user":"user_012345abcdef_123456","booking":false,"offer":false,"pay":false,"date_accounting":"2026-10-15T10:00:00+02:00","type":"t_manualtrans","canceled":false,"mustpay":false,"waiting_list":false}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"number":{"type":"integer"},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["offer_012345abcdef_123456"]},{"const":false}]},"pay":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},{"const":false}]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"userdata":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true},"balance_allocation":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"counterpart_transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"payment_transaction":{"anyOf":[{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},{"const":false}]},"release_reason":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"number":{"type":"integer"},"amount":{"type":"number"},"currency":{"type":"string"},"description":{"type":"string"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"booking":{"anyOf":[{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},{"const":false}]},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["offer_012345abcdef_123456"]},{"const":false}]},"pay":{"anyOf":[{"type":"string","pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["pay_012345abcdef_123456"]},{"const":false}]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"type":{"type":"string"},"canceled":{"type":"boolean"},"mustpay":{"type":"boolean"},"waiting_list":{"type":"boolean"},"userdata":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"}},"additionalProperties":true},"balance_allocation":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"booking":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["booking_012345abcdef_123456"]},"counterpart_transaction":{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},"payment_transaction":{"anyOf":[{"type":"string","pattern":"^paytrans_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["paytrans_012345abcdef_123456"]},{"const":false}]},"release_reason":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/17","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Transaction.php","line":571,"reason":"Requires public user filter, gets candidate ledger IDs, then calls GET_data with an IN filter; it does not invoke the later invoice creation action."},"reason":"Select billable existing user ledger items, including paid membership charges whose invoice is missing; create no invoice.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/17","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"getTransaction","line":439,"branch":{"file":"application/models/v2/Transaction.php","line":571}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/transaction/list/camt Importierte Bankeingänge anzeigen Liest importierte CAMT-Bankeingänge mit Betrag, Währung, Zahlungsreferenz, erkannter Rechnungsnummer und Zuordnungsstatus. id ist eine öffentliche camt-ID; invoice und transaction sind false, solange keine Zuordnung besteht. Unterstützt die allgemeinen Listenparameter. - Operation-ID: get_v2_transaction_list_camt - Dokumentation: /docs/api/v2/operations/get-v2-transaction-list-camt-c870c8ab8ae8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/transaction/list/camt' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/list/camt"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/transaction/list/camt" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_transaction_list_camt","summary":"Importierte Bankeingänge anzeigen","description":"Liest importierte CAMT-Bankeingänge mit Betrag, Währung, Zahlungsreferenz, erkannter Rechnungsnummer und Zuordnungsstatus. id ist eine öffentliche camt-ID; invoice und transaction sind false, solange keine Zuordnung besteht. Unterstützt die allgemeinen Listenparameter.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"transaction_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von CAMT-Bankeingängen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"camt_012345abcdef_123456":{"id":"camt_012345abcdef_123456","invoice":false,"transaction":false,"assigned":"unassigned","date_accounting":"2026-10-15T10:00:00+02:00","date_create":"2026-10-15T10:00:00+02:00","amount":120,"currency":"CHF","additional_info":"Beispieleingang","info":"INV-EXAMPLE","payment_reference":"REFERENCE-EXAMPLE","number":"INV-EXAMPLE"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^camt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["camt_012345abcdef_123456"]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},{"const":false}]},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},{"const":false}]},"assigned":{"type":"string","enum":["unassigned","automatically","manually"]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"amount":{"type":"number"},"currency":{"type":"string"},"additional_info":{"type":"string"},"info":{"type":"string"},"payment_reference":{"type":"string"},"number":{"type":"string"}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^camt_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["camt_012345abcdef_123456"]},"invoice":{"anyOf":[{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["invoice_012345abcdef_123456"]},{"const":false}]},"transaction":{"anyOf":[{"type":"string","pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["transaction_012345abcdef_123456"]},{"const":false}]},"assigned":{"type":"string","enum":["unassigned","automatically","manually"]},"date_accounting":{"anyOf":[{"type":"string"},{"const":false}]},"date_create":{"anyOf":[{"type":"string"},{"const":false}]},"amount":{"type":"number"},"currency":{"type":"string"},"additional_info":{"type":"string"},"info":{"type":"string"},"payment_reference":{"type":"string"},"number":{"type":"string"}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/18","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Transaction.php","line":537,"reason":"Builds SELECT over existing CAMT/transaction/invoice links and calls GET_data; no XML upload/import/assignment write is called."},"reason":"List existing CAMT import/assignment metadata and already-linked invoice references.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/18","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"getTransaction","line":439,"branch":{"file":"application/models/v2/Transaction.php","line":537}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/transaction/list/camt CAMT-Bankauszug importieren Importiert eine hochgeladene camt.053-XML-Datei und speichert neue Bankeingänge. Multipart-Feld camt ist erforderlich; Dateiendung .xml und Größe strikt unter 10.000.000 Bytes. Erkennt Dubletten anhand Zahlungsreferenzen und versucht passende Rechnungen automatisch zu bezahlen, wenn Währung und Voraussetzungen passen. Der Import ist eine Buchungsaktion; nach unklarem Ausgang nicht blind wiederholen. Die Route akzeptiert keinen Ersatz der Datei durch JSON-data. - Operation-ID: post_v2_transaction_list_camt - Dokumentation: /docs/api/v2/operations/post-v2-transaction-list-camt-68e5c7bf5cb6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/transaction/list/camt' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/transaction/list/camt"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/transaction/list/camt" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_transaction_list_camt","summary":"CAMT-Bankauszug importieren","description":"Importiert eine hochgeladene camt.053-XML-Datei und speichert neue Bankeingänge. Multipart-Feld camt ist erforderlich; Dateiendung .xml und Größe strikt unter 10.000.000 Bytes. Erkennt Dubletten anhand Zahlungsreferenzen und versucht passende Rechnungen automatisch zu bezahlen, wenn Währung und Voraussetzungen passen. Der Import ist eine Buchungsaktion; nach unklarem Ausgang nicht blind wiederholen. Die Route akzeptiert keinen Ersatz der Datei durch JSON-data.","tags":["transaction"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"id=false, Dublettenliste und template-Zähler Count1=neue Eingänge, Count2=automatische Zuordnungen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":false,"duplicate_entries":[],"template":{"text_code":"N53036","Count1":3,"Count2":2}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"const":false},"duplicate_entries":{"type":"array","items":{"type":"object","properties":{"additional_info":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"date_accounting":{"type":"string"},"payment_reference":{"type":"string"},"number":{"type":"string"}},"additionalProperties":true}},"template":{"type":"object","properties":{"text_code":{"const":"N53036"},"Count1":{"type":"integer"},"Count2":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"camt":{"type":"string","format":"binary","description":"camt.053-XML-Datei mit .xml-Endung, Größe <10.000.000 Bytes."}},"additionalProperties":true,"required":["camt"]},"encoding":{"camt":{"contentType":"application/xml"}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Transaction.php","method":"postTransaction","line":349,"branch":{"file":"application/models/v2/Transaction.php","line":380}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"transaction","short":"tans","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/user Kunden ohne bestehende Buchungen löschen Markiert einen Kunden als gelöscht. Vorher werden direkte Teilnahmen und nicht stornierte Buchungen als Inhaber geprüft; bestehen solche Beziehungen, wird die Löschung mit activebooking beziehungsweise passivebooking abgelehnt. Ohne Pfad-ID muss id im obersten Requestfeld stehen; eine ID in data.id wählt keinen Kunden aus. - Operation-ID: delete_v2_user - Dokumentation: /docs/api/v2/operations/delete-v2-user-7cc43f2519d8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Legacy query alternative to the JSON delete envelope. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "user_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/user' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "user_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"user_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_user","summary":"Kunden ohne bestehende Buchungen löschen","description":"Markiert einen Kunden als gelöscht. Vorher werden direkte Teilnahmen und nicht stornierte Buchungen als Inhaber geprüft; bestehen solche Beziehungen, wird die Löschung mit activebooking beziehungsweise passivebooking abgelehnt. Ohne Pfad-ID muss id im obersten Requestfeld stehen; eine ID in data.id wählt keinen Kunden aus.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"description":"Legacy query alternative to the JSON delete envelope."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"user_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Kunden ohne bestehende Buchungen löschen","value":{"id":"user_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"deleteUser","line":465},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/user Kundenstammdaten lesen Liest Kunden des ausgewählten Kontos mit Kontakt-, Adress- und konfigurierten data_1 bis data_30-Feldern. Die Pfad-ID setzt einen id-Filter; ohne Pfad-ID steuern filter, fields, order und limit die Sammlung. Angebots-, Wartelisten-, Anfrage- und Mitgliedschaftsfilter können die Kundenliste einschränken. Eine einzelne ID verwendet weiterhin das Sammlungsformat. membership_ids verlangt expression=IN und bis zu 100 öffentliche membershipplan-IDs (Liste oder kommagetrennter Text). membership_active_only akzeptiert per FILTER_VALIDATE_BOOL normalisierte Werte bei expression="="; für diese Filter muss das Mitgliedschaftsmodul sichtbar sein. Bei offerlist sind list_status=open/rejected/expired/accepted und bei offer+waitlist die Werte mustpay/cancelled/waiting/all möglich. fields kann account_balance_CHF oder entsprechende Währungscodes anfordern. - Operation-ID: get_v2_user - Dokumentation: /docs/api/v2/operations/get-v2-user-3fbcf65c535d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"examples":{"accountCustomers":{"value":[{"property":"email","value":"alex@example.invalid"}]},"membership":{"value":[{"property":"membership_ids","expression":"IN","value":["membershipplan_012345abcdef_123456"]},{"property":"membership_active_only","expression":"=","value":true}]}}}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/user' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_user","summary":"Kundenstammdaten lesen","description":"Liest Kunden des ausgewählten Kontos mit Kontakt-, Adress- und konfigurierten data_1 bis data_30-Feldern. Die Pfad-ID setzt einen id-Filter; ohne Pfad-ID steuern filter, fields, order und limit die Sammlung. Angebots-, Wartelisten-, Anfrage- und Mitgliedschaftsfilter können die Kundenliste einschränken. Eine einzelne ID verwendet weiterhin das Sammlungsformat. membership_ids verlangt expression=IN und bis zu 100 öffentliche membershipplan-IDs (Liste oder kommagetrennter Text). membership_active_only akzeptiert per FILTER_VALIDATE_BOOL normalisierte Werte bei expression=\"=\"; für diese Filter muss das Mitgliedschaftsmodul sichtbar sein. Bei offerlist sind list_status=open/rejected/expired/accepted und bei offer+waitlist die Werte mustpay/cancelled/waiting/all möglich. fields kann account_balance_CHF oder entsprechende Währungscodes anfordern.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"examples":{"accountCustomers":{"value":[{"property":"email","value":"alex@example.invalid"}]},"membership":{"value":[{"property":"membership_ids","expression":"IN","value":["membershipplan_012345abcdef_123456"]},{"property":"membership_active_only","expression":"=","value":true}]}}}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"user_012345abcdef_123456":{"id":"user_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-user-handoff":{"condition":{"requestedField":"decision_url","topLevelFilter":"offerlist"},"sensitivePaths":["/data"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/*/decision_url"],"x-edoobox-user-handoff-source":{"file":"application/models/v2/User.php","line":550,"lineEnd":640,"sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958","reason":"Ordinary getUser adds the fixed decision_url only when the first effective top-level offerlist filter is truthy. A request explicitly selecting decision_url under that condition delivers its data map only to the authenticated human through a one-time handoff. Normal reads keep their existing behavior. Stateless link construction remains a read. The embedded date does not establish a documented strict24-hour expiry; consumer validity_date/status checks remain native preconditions. Files/download and dashboard return to different handlers before this branch."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"getUser","line":486},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/user Kundenstammdatensatz erstellen Erstellt einen Kunden samt zugehörigem Login-Datensatz; first_name und last_name sind erforderlich. Ein JSON-Array kann mehrere Kunden anlegen. password ist optional und wird bei Übergabe durch die Passwortregeln geprüft; je nach Konto wird automatisch ein Passwort erzeugt. Barcode- und Geodatenfolgen laufen nach dem Speichern. data_1 bis data_30 sind kontospezifische offene Textfelder. - Operation-ID: post_v2_user - Dokumentation: /docs/api/v2/operations/post-v2-user-3593420b7d26/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "first_name": "Alex", "last_name": "Beispiel", "email": "alex@example.invalid", "language": "de" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/user' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "first_name": "Alex", "last_name": "Beispiel", "email": "alex@example.invalid", "language": "de" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"first_name\":\"Alex\",\"last_name\":\"Beispiel\",\"email\":\"alex@example.invalid\",\"language\":\"de\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_user","summary":"Kundenstammdatensatz erstellen","description":"Erstellt einen Kunden samt zugehörigem Login-Datensatz; first_name und last_name sind erforderlich. Ein JSON-Array kann mehrere Kunden anlegen. password ist optional und wird bei Übergabe durch die Passwortregeln geprüft; je nach Konto wird automatisch ein Passwort erzeugt. Barcode- und Geodatenfolgen laufen nach dem Speichern. data_1 bis data_30 sind kontospezifische offene Textfelder.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Bei Einzelanlage data.id als öffentliche ID; bei Mehrfachanlage als ID-Liste. insert meldet den Schreibstatus.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"user_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]}},{"type":"null"}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"gid":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":46},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":56,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"master":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":65,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","examples":["female"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":84},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":97},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":110},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":119},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":128},"country":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":137},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","examples":["de"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":159},"email":{"x-edoobox-canonicalType":"string","x-edoobox-normalization":"EmailNormalizer trims, lowercases and Unicode-normalizes text; non-string JSON values become empty text. The surrounding aP call truncates storage; no rejecting wire type, maxLength or email-syntax constraint is established.","examples":["alex@example.invalid"],"x-edoobox-sourceLine":168},"avatar_key":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."},"avatar_version":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."},"company":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":201},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}},{"type":"object","additionalProperties":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}}]},"password":{"type":"string","writeOnly":true,"description":"Optionales neues Login-Passwort; gemeinsame Passwortregeln prüfen Mindestlänge, Gross-/Kleinbuchstaben, Ziffer, Sonderzeichen und bekannte schwache Passwörter."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["first_name","last_name"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"gid":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":46},"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":56,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"master":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":65,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","examples":["female"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":84},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":97},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":110},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":119},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":128},"country":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":137},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","examples":["de"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":159},"email":{"x-edoobox-canonicalType":"string","x-edoobox-normalization":"EmailNormalizer trims, lowercases and Unicode-normalizes text; non-string JSON values become empty text. The surrounding aP call truncates storage; no rejecting wire type, maxLength or email-syntax constraint is established.","examples":["alex@example.invalid"],"x-edoobox-sourceLine":168},"avatar_key":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."},"avatar_version":{"readOnly":true,"x-edoobox-ignoredOnCreate":true,"description":"The API skips this insert:false field. It is tolerated for existing Vue model payloads and omitted from useful create examples."},"company":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":201},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}},{"type":"object","additionalProperties":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}}]},"password":{"type":"string","writeOnly":true,"description":"Optionales neues Login-Passwort; gemeinsame Passwortregeln prüfen Mindestlänge, Gross-/Kleinbuchstaben, Ziffer, Sonderzeichen und bekannte schwache Passwörter."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["first_name","last_name"]}}],"x-edoobox-readOnlyFields":["avatar_key","avatar_version"],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kundenstammdatensatz erstellen","value":{"data":{"first_name":"Alex","last_name":"Beispiel","email":"alex@example.invalid","language":"de"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"postUser","line":370},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/user Kundenstammdaten aktualisieren Ändert Kundenfelder, Tags und gegebenenfalls das Login-Passwort. Ohne Pfad-ID ist die öffentliche Kunden-ID im obersten id-Feld erforderlich. password wird vor dem Schreiben geprüft; Adressänderungen führen Geodaten nach. data_1 bis data_30 sind kontospezifisch, unveränderliche Felder werden nicht geändert. - Operation-ID: put_v2_user - Dokumentation: /docs/api/v2/operations/put-v2-user-7a0c2d49aa94/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "first_name": "Alex", "last_name": "Beispiel", "email": "alex@example.invalid" }, "id": "user_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/user' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "first_name": "Alex", "last_name": "Beispiel", "email": "alex@example.invalid" }, "id": "user_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"first_name\":\"Alex\",\"last_name\":\"Beispiel\",\"email\":\"alex@example.invalid\"},\"id\":\"user_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_user","summary":"Kundenstammdaten aktualisieren","description":"Ändert Kundenfelder, Tags und gegebenenfalls das Login-Passwort. Ohne Pfad-ID ist die öffentliche Kunden-ID im obersten id-Feld erforderlich. password wird vor dem Schreiben geprüft; Adressänderungen führen Geodaten nach. data_1 bis data_30 sind kontospezifisch, unveränderliche Felder werden nicht geändert.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"user_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"gid":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":56,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"master":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":65,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":84},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":97},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":110},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":119},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":128},"country":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":137},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":159},"email":{"x-edoobox-canonicalType":"string","x-edoobox-normalization":"EmailNormalizer trims, lowercases and Unicode-normalizes text; non-string JSON values become empty text. The surrounding aP call truncates storage; no rejecting wire type, maxLength or email-syntax constraint is established.","examples":["alex@example.invalid"],"x-edoobox-sourceLine":168},"avatar_key":false,"avatar_version":false,"company":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":201},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}},{"type":"object","additionalProperties":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}}]},"password":{"type":"string","writeOnly":true,"description":"Optionales neues Login-Passwort; gemeinsame Passwortregeln prüfen Mindestlänge, Gross-/Kleinbuchstaben, Ziffer, Sonderzeichen und bekannte schwache Passwörter."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["gid","avatar_key","avatar_version"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Kundenstammdaten aktualisieren","value":{"data":{"first_name":"Alex","last_name":"Beispiel","email":"alex@example.invalid"},"id":"user_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"updateUser","line":419},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/user/{id} Kunden ohne bestehende Buchungen löschen Markiert einen Kunden als gelöscht. Vorher werden direkte Teilnahmen und nicht stornierte Buchungen als Inhaber geprüft; bestehen solche Beziehungen, wird die Löschung mit activebooking beziehungsweise passivebooking abgelehnt. Ohne Pfad-ID muss id im obersten Requestfeld stehen; eine ID in data.id wählt keinen Kunden aus. - Operation-ID: delete_v2_user_id - Dokumentation: /docs/api/v2/operations/delete-v2-user-id-14f78e0a44f5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_user_id","summary":"Kunden ohne bestehende Buchungen löschen","description":"Markiert einen Kunden als gelöscht. Vorher werden direkte Teilnahmen und nicht stornierte Buchungen als Inhaber geprüft; bestehen solche Beziehungen, wird die Löschung mit activebooking beziehungsweise passivebooking abgelehnt. Ohne Pfad-ID muss id im obersten Requestfeld stehen; eine ID in data.id wählt keinen Kunden aus.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"user_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]}},"additionalProperties":true},"examples":{"request":{"summary":"Kunden ohne bestehende Buchungen löschen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"deleteUser","line":465},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/user/{id} Kundenstammdaten lesen Liest Kunden des ausgewählten Kontos mit Kontakt-, Adress- und konfigurierten data_1 bis data_30-Feldern. Die Pfad-ID setzt einen id-Filter; ohne Pfad-ID steuern filter, fields, order und limit die Sammlung. Angebots-, Wartelisten-, Anfrage- und Mitgliedschaftsfilter können die Kundenliste einschränken. Eine einzelne ID verwendet weiterhin das Sammlungsformat. membership_ids verlangt expression=IN und bis zu 100 öffentliche membershipplan-IDs (Liste oder kommagetrennter Text). membership_active_only akzeptiert per FILTER_VALIDATE_BOOL normalisierte Werte bei expression="="; für diese Filter muss das Mitgliedschaftsmodul sichtbar sein. Bei offerlist sind list_status=open/rejected/expired/accepted und bei offer+waitlist die Werte mustpay/cancelled/waiting/all möglich. fields kann account_balance_CHF oder entsprechende Währungscodes anfordern. - Operation-ID: get_v2_user_id - Dokumentation: /docs/api/v2/operations/get-v2-user-id-a89791558ba6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"examples":{"accountCustomers":{"value":[{"property":"email","value":"alex@example.invalid"}]},"membership":{"value":[{"property":"membership_ids","expression":"IN","value":["membershipplan_012345abcdef_123456"]},{"property":"membership_active_only","expression":"=","value":true}]}}}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_user_id","summary":"Kundenstammdaten lesen","description":"Liest Kunden des ausgewählten Kontos mit Kontakt-, Adress- und konfigurierten data_1 bis data_30-Feldern. Die Pfad-ID setzt einen id-Filter; ohne Pfad-ID steuern filter, fields, order und limit die Sammlung. Angebots-, Wartelisten-, Anfrage- und Mitgliedschaftsfilter können die Kundenliste einschränken. Eine einzelne ID verwendet weiterhin das Sammlungsformat. membership_ids verlangt expression=IN und bis zu 100 öffentliche membershipplan-IDs (Liste oder kommagetrennter Text). membership_active_only akzeptiert per FILTER_VALIDATE_BOOL normalisierte Werte bei expression=\"=\"; für diese Filter muss das Mitgliedschaftsmodul sichtbar sein. Bei offerlist sind list_status=open/rejected/expired/accepted und bei offer+waitlist die Werte mustpay/cancelled/waiting/all möglich. fields kann account_balance_CHF oder entsprechende Währungscodes anfordern.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"examples":{"accountCustomers":{"value":[{"property":"email","value":"alex@example.invalid"}]},"membership":{"value":[{"property":"membership_ids","expression":"IN","value":["membershipplan_012345abcdef_123456"]},{"property":"membership_active_only","expression":"=","value":true}]}}}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"user_012345abcdef_123456":{"id":"user_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-user-handoff":{"condition":{"requestedField":"decision_url","topLevelFilter":"offerlist"},"sensitivePaths":["/data"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/*/decision_url"],"x-edoobox-user-handoff-source":{"file":"application/models/v2/User.php","line":550,"lineEnd":640,"sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958","reason":"Ordinary getUser adds the fixed decision_url only when the first effective top-level offerlist filter is truthy. A request explicitly selecting decision_url under that condition delivers its data map only to the authenticated human through a one-time handoff. Normal reads keep their existing behavior. Stateless link construction remains a read. The embedded date does not establish a documented strict24-hour expiry; consumer validity_date/status checks remain native preconditions. Files/download and dashboard return to different handlers before this branch."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"getUser","line":486},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/user/{id} Kundenstammdaten aktualisieren Ändert Kundenfelder, Tags und gegebenenfalls das Login-Passwort. Ohne Pfad-ID ist die öffentliche Kunden-ID im obersten id-Feld erforderlich. password wird vor dem Schreiben geprüft; Adressänderungen führen Geodaten nach. data_1 bis data_30 sind kontospezifisch, unveränderliche Felder werden nicht geändert. - Operation-ID: put_v2_user_id - Dokumentation: /docs/api/v2/operations/put-v2-user-id-ddc272d8fe4a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "first_name": "Alex", "last_name": "Beispiel", "email": "alex@example.invalid" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "first_name": "Alex", "last_name": "Beispiel", "email": "alex@example.invalid" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"first_name\":\"Alex\",\"last_name\":\"Beispiel\",\"email\":\"alex@example.invalid\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_user_id","summary":"Kundenstammdaten aktualisieren","description":"Ändert Kundenfelder, Tags und gegebenenfalls das Login-Passwort. Ohne Pfad-ID ist die öffentliche Kunden-ID im obersten id-Feld erforderlich. password wird vor dem Schreiben geprüft; Adressänderungen führen Geodaten nach. data_1 bis data_30 sind kontospezifisch, unveränderliche Felder werden nicht geändert.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"user_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"gid":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":56,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"master":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":65,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":84},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":97},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":110},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":119},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":128},"country":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":137},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":159},"email":{"x-edoobox-canonicalType":"string","x-edoobox-normalization":"EmailNormalizer trims, lowercases and Unicode-normalizes text; non-string JSON values become empty text. The surrounding aP call truncates storage; no rejecting wire type, maxLength or email-syntax constraint is established.","examples":["alex@example.invalid"],"x-edoobox-sourceLine":168},"avatar_key":false,"avatar_version":false,"company":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":201},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}},{"type":"object","additionalProperties":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}}]},"password":{"type":"string","writeOnly":true,"description":"Optionales neues Login-Passwort; gemeinsame Passwortregeln prüfen Mindestlänge, Gross-/Kleinbuchstaben, Ziffer, Sonderzeichen und bekannte schwache Passwörter."}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["gid","avatar_key","avatar_version"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kundenstammdaten aktualisieren","value":{"data":{"first_name":"Alex","last_name":"Beispiel","email":"alex@example.invalid"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"updateUser","line":419},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/user/{id}/batch_import Kunden und optional Buchungen importieren Importiert höchstens 50 Kunden mit email, first_name und last_name. import_location=users_table importiert Stammdaten; offer benötigt zusätzlich offer_id, pricecategory_id und import_location_in_offer (booking, waiting_list oder offerlist-ID). handle_duplicate_users steuert Ergänzen, Überschreiben oder Neuanlegen; ausser bei ignore_existing sind duplicate_id und handle_duplicate_user_bookings nötig. send_emails muss ein JSON-Boolean sein und kann einen Versandauftrag anlegen. Für Anfrage-Listen werden Angebot und aktive Preiskategorie geprüft; Lektionenangebote unterstützen diesen Listenimport nicht. - Operation-ID: post_v2_user_id_batch_import - Dokumentation: /docs/api/v2/operations/post-v2-user-id-batch-import-7039d95d6053/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Dieser Alias wertet den Pfadwert nicht als Kunden-ID aus; bevorzugt /user/batch/batch_import verwenden. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Dieser Alias wertet den Pfadwert nicht als Kunden-ID aus; bevorzugt /user/batch/batch_import verwenden.","schema":{"type":"string"},"example":"batch"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "import_location": "users_table", "handle_duplicate_users": "ignore_existing", "send_emails": false, "users": [ { "email": "alex@example.invalid", "first_name": "Alex", "last_name": "Beispiel" } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'/batch_import' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}/batch_import"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "import_location": "users_table", "handle_duplicate_users": "ignore_existing", "send_emails": false, "users": [ { "email": "alex@example.invalid", "first_name": "Alex", "last_name": "Beispiel" } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}/batch_import" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"import_location\":\"users_table\",\"handle_duplicate_users\":\"ignore_existing\",\"send_emails\":false,\"users\":[{\"email\":\"alex@example.invalid\",\"first_name\":\"Alex\",\"last_name\":\"Beispiel\"}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_user_id_batch_import","summary":"Kunden und optional Buchungen importieren","description":"Importiert höchstens 50 Kunden mit email, first_name und last_name. import_location=users_table importiert Stammdaten; offer benötigt zusätzlich offer_id, pricecategory_id und import_location_in_offer (booking, waiting_list oder offerlist-ID). handle_duplicate_users steuert Ergänzen, Überschreiben oder Neuanlegen; ausser bei ignore_existing sind duplicate_id und handle_duplicate_user_bookings nötig. send_emails muss ein JSON-Boolean sein und kann einen Versandauftrag anlegen. Für Anfrage-Listen werden Angebot und aktive Preiskategorie geprüft; Lektionenangebote unterstützen diesen Listenimport nicht.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Dieser Alias wertet den Pfadwert nicht als Kunden-ID aus; bevorzugt /user/batch/batch_import verwenden.","schema":{"type":"string"},"example":"batch"},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.ids enthält erstellte beziehungsweise nachgeführte Kunden-IDs; data.insert=true meldet den abgeschlossenen Import. Ein Versandauftrag wird gegebenenfalls zusätzlich erstellt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"ids":["user_012345abcdef_123456"],"insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"import_location":{"type":"string","enum":["offer","users_table"]},"handle_duplicate_users":{"type":"string","enum":["extend_existing","overwrite","ignore_existing"]},"send_emails":{"type":"boolean"},"users":{"type":"array","items":{"type":"object","properties":{"email":{"x-edoobox-canonicalType":"string","description":"E-Mail-Feld muss vorhanden und nicht null sein; normale Kundenvalidierung/Normalisierung gilt."},"first_name":{"x-edoobox-canonicalType":"string","description":"Pflichtfeld nach Feldvorhandensein; allgemeine Kunden-Normalisierung gilt."},"last_name":{"x-edoobox-canonicalType":"string","description":"Pflichtfeld nach Feldvorhandensein; allgemeine Kunden-Normalisierung gilt."},"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","examples":["country_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"language":{"type":"string"},"gender":{"type":"string"}},"additionalProperties":true,"required":["email","first_name","last_name"],"description":"Weitere Kundenfelder entsprechend dem User-Schema, einschliesslich data_1 bis data_30; kein accountabhängiges Feld wird zusätzlich als Pflichtfeld erfunden."},"minItems":1,"maxItems":50},"offer_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"pricecategory_id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"import_location_in_offer":{"anyOf":[{"enum":["booking","waiting_list"]},{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","examples":["offerlist_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"duplicate_id":{"type":"string","enum":["user_id","email"]},"handle_duplicate_user_bookings":{"type":"string","enum":["ignore","add_additional_booking"]}},"additionalProperties":true,"required":["import_location","handle_duplicate_users","send_emails","users"],"allOf":[{"if":{"properties":{"import_location":{"const":"offer"}},"required":["import_location"]},"then":{"required":["offer_id","pricecategory_id","import_location_in_offer"]}},{"if":{"properties":{"handle_duplicate_users":{"enum":["extend_existing","overwrite"]}},"required":["handle_duplicate_users"]},"then":{"required":["duplicate_id","handle_duplicate_user_bookings"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kunden und optional Buchungen importieren","value":{"data":{"import_location":"users_table","handle_duplicate_users":"ignore_existing","send_emails":false,"users":[{"email":"alex@example.invalid","first_name":"Alex","last_name":"Beispiel"}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":["Für einen reinen Stammdatenimport verwendet das Beispiel send_emails=false. Versand und Buchungsfolgen benötigen einen passenden Angebotskontext und werden nativ geprüft."]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"postUser","line":370,"branch":{"file":"application/models/v2/User.php","line":390}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/user/{id}/dashboard Kundenübersicht mit Buchungen und Salden laden Lädt Stammdaten, eigene und Sammelteilnehmer-Buchungen, Teilnahmen, Angebote, Rechnungen, Salden, vorhandenen Barcode, Dateien und gespeicherte Geokoordinaten. Die Pfad-ID muss eine user-ID sein. Barcode und Geocode werden hier nur gelesen; fehlende Einträge werden nicht erzeugt. - Operation-ID: get_v2_user_id_dashboard - Dokumentation: /docs/api/v2/operations/get-v2-user-id-dashboard-4a9fb74cd082/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'/dashboard' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}/dashboard"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}/dashboard" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_user_id_dashboard","summary":"Kundenübersicht mit Buchungen und Salden laden","description":"Lädt Stammdaten, eigene und Sammelteilnehmer-Buchungen, Teilnahmen, Angebote, Rechnungen, Salden, vorhandenen Barcode, Dateien und gespeicherte Geokoordinaten. Die Pfad-ID muss eine user-ID sein. Barcode und Geocode werden hier nur gelesen; fehlende Einträge werden nicht erzeugt.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Erweiterte fachliche Rückgabe in data; die aufgeführten Sammlungen können leer sein und enthalten ausschliesslich öffentliche Referenzen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"balances":[{"total":0,"currency_iso":"CHF"}],"barcode":false,"bookings":[],"bookings_groupparticipant":[],"invoices":[],"participation":[],"user":{"id":"user_012345abcdef_123456","first_name":"Alex","last_name":"Beispiel","email":"alex@example.invalid"},"files":false,"geocode":false,"offers":[],"user_id":"user_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"balances":{"type":"array","items":{"type":"object","properties":{"total":{"type":"number"},"currency_iso":{"type":"string"}},"additionalProperties":true}},"barcode":{"anyOf":[{"type":"object","properties":{"code":{"type":"string"},"id":{"type":"string","pattern":"^barcode_[a-f0-9]{12}_-?[0-9]+$","examples":["barcode_012345abcdef_123456"]},"type":{"type":"string"}},"additionalProperties":true},{"const":false}]},"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"time":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"number":{"type":["number","string"]},"amount":{"type":"number"},"currency":{"type":"string"},"mustpay":{"type":"boolean"},"canceled":{"type":"boolean"},"waiting_list":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"promotion_id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"]},"promotion":{"type":"object","properties":{"name":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},"bookings_groupparticipant":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"time":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"number":{"type":["number","string"]},"amount":{"type":"number"},"currency":{"type":"string"},"mustpay":{"type":"boolean"},"canceled":{"type":"boolean"},"waiting_list":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"promotion_id":{"type":"string","pattern":"^promotion_[a-f0-9]{12}_-?[0-9]+$","examples":["promotion_012345abcdef_123456"]},"promotion":{"type":"object","properties":{"name":{"type":"string"},"code":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}},"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","examples":["invoice_012345abcdef_123456"]},"amount":{"type":"number"},"amount_pay":{"type":"number"},"currency":{"type":"string"},"status":{"type":"integer"},"number":{"type":"string"},"date_create":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"date_payuntil":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"refresh_suggested":{"type":"boolean"}},"additionalProperties":true}},"participation":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","examples":["booking_012345abcdef_123456"]},"waiting_list":{"type":"boolean"},"offer":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}},"additionalProperties":true}},"user":{"type":"object","properties":{},"additionalProperties":true,"description":"Kundenstammdaten aus dem User-Structure, einschliesslich kontoabhängiger Zusatzfelder."},"files":{"description":"Ergebnis der internen File-Ressourcenabfrage; kann false oder eine Sammlung sein.","anyOf":[{"anyOf":[{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}},{"type":"object","additionalProperties":{"type":"object","properties":{},"additionalProperties":true,"description":"Datensatz mit den jeweils konfigurierten Feldern des verbundenen Ressourcentyps."}}]},{"const":false}]},"geocode":{"anyOf":[{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}},"additionalProperties":true},{"const":false}]},"offers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]},"category":{"anyOf":[{"type":"string","pattern":"^category_[a-f0-9]{12}_-?[0-9]+$","examples":["category_012345abcdef_123456"]},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"]}]},"name":{"type":"string"},"number":{"type":"string"},"status":{"type":"integer"},"date_start":{"description":"ISO-8601-Zeitwert von dQ; bei fehlendem/ungültigem gespeicherten Wert false.","anyOf":[{"type":"string","format":"date-time"},{"const":false}]},"archive":{"type":"boolean"},"trash":{"type":"boolean"},"type":{"type":"string"}},"additionalProperties":true}},"user_id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/dashboard/Userdashboard.php","line":9},"reason":"Reviewed complete reachable application callgraph: dashboard SELECTs/formatting, read-only geocode cache lookup, existing ticket path and normal User/File GET_data with action segments cleared. No geocode sync, file URL access logging or ticket generation is invoked.","relatedSources":[{"file":"application/models/Usergeocode.php","line":53},{"file":"application/helpers/edooboxhelp_helper.php","line":9991},{"file":"application/models/v2/Eddata.php","line":18}]},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"getUser","line":486,"branch":{"file":"application/models/v2/User.php","line":501}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/user/{id}/files Datei als gelöscht markieren Markiert die Datei mit der öffentlichen files-ID als gelöscht; der Pfad enthält keine Kunden-ID. Custom-Exportdateien sind über diesen allgemeinen Dateihandler ausgeschlossen. - Operation-ID: delete_v2_user_id_files - Dokumentation: /docs/api/v2/operations/delete-v2-user-id-files-e753d1dc7200/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Datei-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Datei-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_user_id_files","summary":"Datei als gelöscht markieren","description":"Markiert die Datei mit der öffentlichen files-ID als gelöscht; der Pfad enthält keine Kunden-ID. Custom-Exportdateien sind über diesen allgemeinen Dateihandler ausgeschlossen.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Datei-ID.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success sowie fachliche Statusfelder prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Datei als gelöscht markieren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"deleteUser","line":465,"branch":{"file":"application/models/v2/User.php","line":470}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/user/{id}/files Kundendateien und Dateimetadaten lesen Listet nicht gelöschte Dateien mit resource_type=user; Custom-Exportdateien sind ausgeschlossen. Der Pfadwert id in /user/{id}/files wird als Datei-ID (files_...) gefiltert, nicht als Eigentümer-ID. Für die Dateien eines Kunden /user/list/files/list mit filter auf resource_id und der user-ID verwenden. fields, order, limit und response_type gelten wie bei allgemeinen Sammlungen. - Operation-ID: get_v2_user_id_files - Dokumentation: /docs/api/v2/operations/get-v2-user-id-files-71d1f4841fc6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Die Pfad-ID filtert Files.id, nicht den Eigentümer user. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Die Pfad-ID filtert Files.id, nicht den Eigentümer user.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"user_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_user_id_files","summary":"Kundendateien und Dateimetadaten lesen","description":"Listet nicht gelöschte Dateien mit resource_type=user; Custom-Exportdateien sind ausgeschlossen. Der Pfadwert id in /user/{id}/files wird als Datei-ID (files_...) gefiltert, nicht als Eigentümer-ID. Für die Dateien eines Kunden /user/list/files/list mit filter auf resource_id und der user-ID verwenden. fields, order, limit und response_type gelten wie bei allgemeinen Sammlungen.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Die Pfad-ID filtert Files.id, nicht den Eigentümer user.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"user_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmässig eine nach files-ID indizierte Sammlung; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"files_012345abcdef_123456":{"id":"files_012345abcdef_123456","filename":"Unterlagen.pdf","description":{"de":"Kursunterlagen"},"filetype":"avatar","resource_type":"user","resource_id":"user_012345abcdef_123456","last_change":"2026-10-22T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"string"}},{"type":"array","items":{}}]},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"const":false}]},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"string"}},{"type":"array","items":{}}]},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"const":false}]},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"getUser","line":486,"branch":{"file":"application/models/v2/User.php","line":490}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/user/{id}/files Dateimetadaten aktualisieren Ändert filename beziehungsweise die sprachabhängige description einer Datei. Die Pfad-ID ist eine öffentliche files-ID, keine user-ID. resource_type, resource_id und filetype sind unveränderlich; Dateiinhalt wird hier nicht hochgeladen. Custom-Exportdateien sind ausgeschlossen. - Operation-ID: put_v2_user_id_files - Dokumentation: /docs/api/v2/operations/put-v2-user-id-files-da6ade36121a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Datei-ID; der Handler bearbeitet eine Datei, keinen Kunden. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Datei-ID; der Handler bearbeitet eine Datei, keinen Kunden.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": { "de": "Aktualisierte Kursunterlagen" } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}/files"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": { "de": "Aktualisierte Kursunterlagen" } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}/files" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":{\"de\":\"Aktualisierte Kursunterlagen\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_user_id_files","summary":"Dateimetadaten aktualisieren","description":"Ändert filename beziehungsweise die sprachabhängige description einer Datei. Die Pfad-ID ist eine öffentliche files-ID, keine user-ID. resource_type, resource_id und filetype sind unveränderlich; Dateiinhalt wird hier nicht hochgeladen. Custom-Exportdateien sind ausgeschlossen.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Datei-ID; der Handler bearbeitet eine Datei, keinen Kunden.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success sowie fachliche Statusfelder prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"filename":{"x-edoobox-canonicalType":"string","description":"aP-Normalisierung auf gespeicherten Text."},"description":{"type":"object","properties":{},"additionalProperties":{"x-edoobox-canonicalType":"string","description":"Text je unterstützter Sprache."},"description":"Sprachcode als Schlüssel, Text als Wert; nur unterstützte Sprachcodes werden gespeichert, normalisiert und auf 500 Zeichen begrenzt."}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dateimetadaten aktualisieren","value":{"data":{"description":{"de":"Aktualisierte Kursunterlagen"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"updateUser","line":419,"branch":{"file":"application/models/v2/User.php","line":424}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/user/{id}/unnamed Unbenannten Buchungsplatz mit Kundendaten aktivieren Aktiviert einen bisher unbenannten Kunden innerhalb von booking_id und speichert anschliessend die übergebenen Kundenfelder. Die Pfad-ID ist eine user-ID; booking_id muss die Buchung des unbenannten Teilnehmerplatzes bezeichnen. Die Umwandlung kann bereits vor der allgemeinen Stammdatenänderung erfolgen; Fehler erfordern eine erneute Prüfung des Zustands. - Operation-ID: put_v2_user_id_unnamed - Dokumentation: /docs/api/v2/operations/put-v2-user-id-unnamed-f1d9419b60f5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "booking_id": "booking_012345abcdef_123456", "first_name": "Alex", "last_name": "Beispiel", "email": "alex@example.invalid" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/user/'"${PATH_ID}"'/unnamed' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/{id}/unnamed"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "booking_id": "booking_012345abcdef_123456", "first_name": "Alex", "last_name": "Beispiel", "email": "alex@example.invalid" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/{id}/unnamed" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"booking_id\":\"booking_012345abcdef_123456\",\"first_name\":\"Alex\",\"last_name\":\"Beispiel\",\"email\":\"alex@example.invalid\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_user_id_unnamed","summary":"Unbenannten Buchungsplatz mit Kundendaten aktivieren","description":"Aktiviert einen bisher unbenannten Kunden innerhalb von booking_id und speichert anschliessend die übergebenen Kundenfelder. Die Pfad-ID ist eine user-ID; booking_id muss die Buchung des unbenannten Teilnehmerplatzes bezeichnen. Die Umwandlung kann bereits vor der allgemeinen Stammdatenänderung erfolgen; Fehler erfordern eine erneute Prüfung des Zustands.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche fachliche Rückgabe in data; zusätzlich error und success prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"user_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"gid":false,"status":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":56,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"master":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":65,"x-edoobox-canonicalType":"boolean","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[false]},"gender":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","description":"Recognized case-sensitive strings w/female, m/male and d/divers normalize to w, m and d for storage. Every other scalar key normalizes to d. Input is not a closed enum; output uses female, male or divers.","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"first_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":84},"last_name":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":97},"street":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":110},"postcode":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":119},"location":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":120,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":128},"country":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}],"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":137},"language":{"type":["string","number","boolean","null"],"x-edoobox-canonicalType":"string","x-edoobox-schemaStatus":"reviewed-normalizing","x-edoobox-normalization":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","description":"Exact configured language key selects its internal language entry; unknown scalar keys normalize to the configured German fallback (entry 1). No trim/case folding or rejecting code enum is implemented.","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"ccb043758defa22ad8cf879c4c81b317fd537f8fd0cfb125a4c1c7ffc7cdf8bf"}]},"phone_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":20,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":159},"email":{"x-edoobox-canonicalType":"string","x-edoobox-normalization":"EmailNormalizer trims, lowercases and Unicode-normalizes text; non-string JSON values become empty text. The surrounding aP call truncates storage; no rejecting wire type, maxLength or email-syntax constraint is established.","examples":["alex@example.invalid"],"x-edoobox-sourceLine":168},"avatar_key":false,"avatar_version":false,"company":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-sourceLine":201},"tags":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}},{"type":"object","additionalProperties":{"type":"object","properties":{"tag":{"type":"string","pattern":"^tag_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["tag_012345abcdef_123456"]},"type":{"type":"string","description":"normal wird zugeordnet; andere Textwerte werden im aktuellen Hook ignoriert, sind kein schliessendes Enum."}},"additionalProperties":true,"required":["tag","type"]}}]},"password":{"type":"string","writeOnly":true,"description":"Optionales neues Login-Passwort; gemeinsame Passwortregeln prüfen Mindestlänge, Gross-/Kleinbuchstaben, Ziffer, Sonderzeichen und bekannte schwache Passwörter."},"booking_id":{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["gid","avatar_key","avatar_version"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns.","required":["booking_id"]},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Unbenannten Buchungsplatz mit Kundendaten aktivieren","value":{"data":{"booking_id":"booking_012345abcdef_123456","first_name":"Alex","last_name":"Beispiel","email":"alex@example.invalid"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"updateUser","line":419,"branch":{"file":"application/models/v2/User.php","line":438}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/user/batch/batch_import Kunden und optional Buchungen importieren Importiert höchstens 50 Kunden mit email, first_name und last_name. import_location=users_table importiert Stammdaten; offer benötigt zusätzlich offer_id, pricecategory_id und import_location_in_offer (booking, waiting_list oder offerlist-ID). handle_duplicate_users steuert Ergänzen, Überschreiben oder Neuanlegen; ausser bei ignore_existing sind duplicate_id und handle_duplicate_user_bookings nötig. send_emails muss ein JSON-Boolean sein und kann einen Versandauftrag anlegen. Für Anfrage-Listen werden Angebot und aktive Preiskategorie geprüft; Lektionenangebote unterstützen diesen Listenimport nicht. - Operation-ID: post_v2_user_batch_batch_import - Dokumentation: /docs/api/v2/operations/post-v2-user-batch-batch-import-905235419fff/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "import_location": "users_table", "handle_duplicate_users": "ignore_existing", "send_emails": false, "users": [ { "email": "alex@example.invalid", "first_name": "Alex", "last_name": "Beispiel" } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/user/batch/batch_import' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/batch/batch_import"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "import_location": "users_table", "handle_duplicate_users": "ignore_existing", "send_emails": false, "users": [ { "email": "alex@example.invalid", "first_name": "Alex", "last_name": "Beispiel" } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/batch/batch_import" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"import_location\":\"users_table\",\"handle_duplicate_users\":\"ignore_existing\",\"send_emails\":false,\"users\":[{\"email\":\"alex@example.invalid\",\"first_name\":\"Alex\",\"last_name\":\"Beispiel\"}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_user_batch_batch_import","summary":"Kunden und optional Buchungen importieren","description":"Importiert höchstens 50 Kunden mit email, first_name und last_name. import_location=users_table importiert Stammdaten; offer benötigt zusätzlich offer_id, pricecategory_id und import_location_in_offer (booking, waiting_list oder offerlist-ID). handle_duplicate_users steuert Ergänzen, Überschreiben oder Neuanlegen; ausser bei ignore_existing sind duplicate_id und handle_duplicate_user_bookings nötig. send_emails muss ein JSON-Boolean sein und kann einen Versandauftrag anlegen. Für Anfrage-Listen werden Angebot und aktive Preiskategorie geprüft; Lektionenangebote unterstützen diesen Listenimport nicht.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.ids enthält erstellte beziehungsweise nachgeführte Kunden-IDs; data.insert=true meldet den abgeschlossenen Import. Ein Versandauftrag wird gegebenenfalls zusätzlich erstellt.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"ids":["user_012345abcdef_123456"],"insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"import_location":{"type":"string","enum":["offer","users_table"]},"handle_duplicate_users":{"type":"string","enum":["extend_existing","overwrite","ignore_existing"]},"send_emails":{"type":"boolean"},"users":{"type":"array","items":{"type":"object","properties":{"email":{"x-edoobox-canonicalType":"string","description":"E-Mail-Feld muss vorhanden und nicht null sein; normale Kundenvalidierung/Normalisierung gilt."},"first_name":{"x-edoobox-canonicalType":"string","description":"Pflichtfeld nach Feldvorhandensein; allgemeine Kunden-Normalisierung gilt."},"last_name":{"x-edoobox-canonicalType":"string","description":"Pflichtfeld nach Feldvorhandensein; allgemeine Kunden-Normalisierung gilt."},"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","examples":["country_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"language":{"type":"string"},"gender":{"type":"string"}},"additionalProperties":true,"required":["email","first_name","last_name"],"description":"Weitere Kundenfelder entsprechend dem User-Schema, einschliesslich data_1 bis data_30; kein accountabhängiges Feld wird zusätzlich als Pflichtfeld erfunden."},"minItems":1,"maxItems":50},"offer_id":{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"pricecategory_id":{"type":"string","pattern":"^pricecategory_[a-f0-9]{12}_-?[0-9]+$","examples":["pricecategory_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"import_location_in_offer":{"anyOf":[{"enum":["booking","waiting_list"]},{"type":"string","pattern":"^offerlist_[a-f0-9]{12}_-?[0-9]+$","examples":["offerlist_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"duplicate_id":{"type":"string","enum":["user_id","email"]},"handle_duplicate_user_bookings":{"type":"string","enum":["ignore","add_additional_booking"]}},"additionalProperties":true,"required":["import_location","handle_duplicate_users","send_emails","users"],"allOf":[{"if":{"properties":{"import_location":{"const":"offer"}},"required":["import_location"]},"then":{"required":["offer_id","pricecategory_id","import_location_in_offer"]}},{"if":{"properties":{"handle_duplicate_users":{"enum":["extend_existing","overwrite"]}},"required":["handle_duplicate_users"]},"then":{"required":["duplicate_id","handle_duplicate_user_bookings"]}}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kunden und optional Buchungen importieren","value":{"data":{"import_location":"users_table","handle_duplicate_users":"ignore_existing","send_emails":false,"users":[{"email":"alex@example.invalid","first_name":"Alex","last_name":"Beispiel"}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":["Für einen reinen Stammdatenimport verwendet das Beispiel send_emails=false. Versand und Buchungsfolgen benötigen einen passenden Angebotskontext und werden nativ geprüft."]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"postUser","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/user/list Kundenstammdaten lesen Liest Kunden des ausgewählten Kontos mit Kontakt-, Adress- und konfigurierten data_1 bis data_30-Feldern. Die Pfad-ID setzt einen id-Filter; ohne Pfad-ID steuern filter, fields, order und limit die Sammlung. Angebots-, Wartelisten-, Anfrage- und Mitgliedschaftsfilter können die Kundenliste einschränken. Eine einzelne ID verwendet weiterhin das Sammlungsformat. membership_ids verlangt expression=IN und bis zu 100 öffentliche membershipplan-IDs (Liste oder kommagetrennter Text). membership_active_only akzeptiert per FILTER_VALIDATE_BOOL normalisierte Werte bei expression="="; für diese Filter muss das Mitgliedschaftsmodul sichtbar sein. Bei offerlist sind list_status=open/rejected/expired/accepted und bei offer+waitlist die Werte mustpay/cancelled/waiting/all möglich. fields kann account_balance_CHF oder entsprechende Währungscodes anfordern. - Operation-ID: get_v2_user_list - Dokumentation: /docs/api/v2/operations/get-v2-user-list-d08972c27c71/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"examples":{"accountCustomers":{"value":[{"property":"email","value":"alex@example.invalid"}]},"membership":{"value":[{"property":"membership_ids","expression":"IN","value":["membershipplan_012345abcdef_123456"]},{"property":"membership_active_only","expression":"=","value":true}]}}}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/user/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_user_list","summary":"Kundenstammdaten lesen","description":"Liest Kunden des ausgewählten Kontos mit Kontakt-, Adress- und konfigurierten data_1 bis data_30-Feldern. Die Pfad-ID setzt einen id-Filter; ohne Pfad-ID steuern filter, fields, order und limit die Sammlung. Angebots-, Wartelisten-, Anfrage- und Mitgliedschaftsfilter können die Kundenliste einschränken. Eine einzelne ID verwendet weiterhin das Sammlungsformat. membership_ids verlangt expression=IN und bis zu 100 öffentliche membershipplan-IDs (Liste oder kommagetrennter Text). membership_active_only akzeptiert per FILTER_VALIDATE_BOOL normalisierte Werte bei expression=\"=\"; für diese Filter muss das Mitgliedschaftsmodul sichtbar sein. Bei offerlist sind list_status=open/rejected/expired/accepted und bei offer+waitlist die Werte mustpay/cancelled/waiting/all möglich. fields kann account_balance_CHF oder entsprechende Währungscodes anfordern.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Allgemeine Filter plus Angebot/Warteliste/Anfrageliste und die beschriebenen Mitgliedschaftsfilter. IDs bleiben öffentliche v2-IDs.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"examples":{"accountCustomers":{"value":[{"property":"email","value":"alex@example.invalid"}]},"membership":{"value":[{"property":"membership_ids","expression":"IN","value":["membershipplan_012345abcdef_123456"]},{"property":"membership_active_only","expression":"=","value":true}]}}}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Response; inspect error and success as legacy failures may also use HTTP 200.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"user_012345abcdef_123456":{"id":"user_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["user_012345abcdef_123456"]},"gid":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":46,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["gid_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":56,"type":"boolean"},"master":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":65,"type":"boolean"},"gender":{"type":"string","enum":["female","male","divers"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":74,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/edooboxhelp_helper.php","line":9093,"reason":"Followed getGenderWMD: aliases, case-sensitive lookup, scalar fallback and en2 output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"first_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":84,"type":"string"},"last_name":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":97,"type":"string"},"street":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":110,"type":"string"},"postcode":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":119,"type":"string"},"location":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":128,"type":"string"},"country":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":137,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"language":{"type":["string","null"],"x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":150,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/helpers/langedoobox_helper.php","line":731,"reason":"Input edLangIDByName uses alllanguage array key lookup, otherwise internal fallback 1.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/helpers/langedoobox_helper.php","line":566,"reason":"Output edLangById finds the matching configured key; no match falls through to null.","sha256":"6454b27d47a74f3cf236ef8e258fbe821228c1607165d00433a309b2e9127d3c"},{"file":"application/config/config.php","line":93,"reason":"Reviewed alllanguage configuration; German is fallback entry and language keys are configuration-driven.","sha256":"8d7bcae1a154ab06f54007072f84af5fe87ae7349b1b06738e7adbd8ee084f82"},{"file":"application/models/v2/Userfunc.php","line":16,"reason":"Before-insert hook copies normalized DataUP into the global-user row without changing language/gender.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":67,"reason":"Before-update hook copies normalized DataUP into the global-user row; it does not validate language/gender differently.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/Userfunc.php","line":198,"reason":"After hooks schedule barcode/geocoding side effects; no alternative reviewed-field normalization.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/Usergeocode.php","line":10,"reason":"Followed geocoding entry point: it reads address data and persists geocoding cache, not a replacement language/gender value.","sha256":"45bcccf55bf7a20f5d7621e01f749f95c30dd79161b180403c28a330808a7b12"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"phone_number":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":159,"type":"string"},"email":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":168},"avatar_key":{"type":"string","readOnly":true,"pattern":"^[A-Za-z0-9_]*$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":177,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":90,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"avatar_version":{"type":"string","readOnly":true,"pattern":"^(?:-?[0-9]+)?$","x-edoobox-contractSource":[{"file":"application/models/v2/User.php","line":189,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/libraries/edoobox/Avatar/AvatarField.php","line":112,"reason":"Followed shared avatar response conversion.","sha256":"bf1d21c20dbd32576aadf2f3c60a45e3f4021586c2784a9b91e397b92f89790a"},{"file":"application/models/v2/User.php","line":370,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/User.php","line":419,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"company":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":201,"type":"string"},"tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque v2 ID returned for a record in the selected account."},"type":{"type":"string","enum":["normal","include","exclude",""]}},"additionalProperties":true,"required":["tag","type"]},"x-edoobox-contractSource":[{"file":"application/models/v2/Userfunc.php","line":859,"reason":"Callback consumes an array of {tag,type} records instead of the aP get placeholder.","sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c"},{"file":"application/models/v2/User.php","line":226,"reason":"Response builds an array of tag/type objects, not a scalar encoded ID.","sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958"},{"file":"application/models/v2/Userfunc.php","line":882,"lineEnd":904,"sha256":"4080444b287dd0c0f054129463c0abf27dd26d86b38b0ab3ca097e40d817b22c","reason":"All supplied tag IDs, including ignored string-mode rows, must be typed same-account existing tags before participant-tag relation changes."}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-user-handoff":{"condition":{"requestedField":"decision_url","topLevelFilter":"offerlist"},"sensitivePaths":["/data"],"oneTime":true},"x-edoobox-sensitive-response-paths":["/data/*/decision_url"],"x-edoobox-user-handoff-source":{"file":"application/models/v2/User.php","line":550,"lineEnd":640,"sha256":"a3f95d9f8fd27ed5b76ef93b0f36fe791a9fbfb73636eb84661093b31feec958","reason":"Ordinary getUser adds the fixed decision_url only when the first effective top-level offerlist filter is truthy. A request explicitly selecting decision_url under that condition delivers its data map only to the authenticated human through a one-time handoff. Normal reads keep their existing behavior. Stateless link construction remains a read. The embedded date does not establish a documented strict24-hour expiry; consumer validity_date/status checks remain native preconditions. Files/download and dashboard return to different handlers before this branch."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"getUser","line":486},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/user/list/files Datei zu einer Ressource hochladen Lädt file als Multipartdatei hoch. meta ist ein JSON-Text mit dem data-Objekt, resource_type, resource_id und filetype; die Benutzerroute delegiert an den allgemeinen Dateihandler. resource_id muss zum angegebenen Ressourcentyp und Konto gehören. Avatar ersetzt ein vorhandenes Avatar und erlaubt JPEG/PNG/WebP bis 8 MiB; sign und image2/image3 erlauben Bilder bis 2 MiB; interne/externe Angebotsdateien bis 20 MiB. image1 wird vom Upload-Hook nicht akzeptiert. - Operation-ID: post_v2_user_list_files - Dokumentation: /docs/api/v2/operations/post-v2-user-list-files-9fd7d2fac1be/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/user/list/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/list/files"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/user/list/files" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_user_list_files","summary":"Datei zu einer Ressource hochladen","description":"Lädt file als Multipartdatei hoch. meta ist ein JSON-Text mit dem data-Objekt, resource_type, resource_id und filetype; die Benutzerroute delegiert an den allgemeinen Dateihandler. resource_id muss zum angegebenen Ressourcentyp und Konto gehören. Avatar ersetzt ein vorhandenes Avatar und erlaubt JPEG/PNG/WebP bis 8 MiB; sign und image2/image3 erlauben Bilder bis 2 MiB; interne/externe Angebotsdateien bis 20 MiB. image1 wird vom Upload-Hook nicht akzeptiert.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success sowie fachliche Statusfelder prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"meta":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"object","properties":{"data":{"type":"object","properties":{"resource_type":{"type":"string","enum":["admin","user","offer"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"filetype":{"type":"string","enum":["avatar","sign","image2","image3","internalOfferFile","externalOfferFile"]},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":{"x-edoobox-canonicalType":"string","description":"Text je unterstützter Sprache."},"description":"Sprachcode als Schlüssel, Text als Wert; nur unterstützte Sprachcodes werden gespeichert, normalisiert und auf 500 Zeichen begrenzt."}},"additionalProperties":true,"required":["resource_type","resource_id","filetype"]}},"additionalProperties":true,"required":["data"]},"description":"JSON-Text mit vollständigem data-Metadatenobjekt; ersetzt im Dateihandler die Form-Metadaten."},"resource_type":{"type":"string","enum":["admin","user","offer"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"filetype":{"type":"string","enum":["avatar","sign","image2","image3","internalOfferFile","externalOfferFile"]},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":{"x-edoobox-canonicalType":"string","description":"Text je unterstützter Sprache."},"description":"Sprachcode als Schlüssel, Text als Wert; nur unterstützte Sprachcodes werden gespeichert, normalisiert und auf 500 Zeichen begrenzt."}},"additionalProperties":true,"required":["file"],"anyOf":[{"required":["meta"]},{"required":["resource_type","resource_id","filetype"]}]},"examples":{"avatar":{"summary":"Synthetische Metadaten; echte Datei-Bytes und Konto-ID verwenden.","value":{"file":"","meta":"{\"data\":{\"resource_type\":\"user\",\"resource_id\":\"user_012345abcdef_123456\",\"filetype\":\"avatar\",\"filename\":\"avatar.png\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"postUser","line":370,"branch":{"file":"application/models/v2/User.php","line":377}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/user/list/files/{fileId}/download Kundendatei herunterladen Lädt die Datei mit fileId als direktem Dateidownload; die öffentliche files-ID muss zum ausgewählten Konto gehören. Gelöschte Dateien und Custom-Exportdateien sind ausgeschlossen. Ein nicht vorhandener Eintrag oder fehlende Dateibytes führt zu file_not_found. - Operation-ID: get_v2_user_list_files_fileId_download - Dokumentation: /docs/api/v2/operations/get-v2-user-list-files-fileid-download-ee2a8993f235/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fileId | path | ja | Opaque route segment accepted by this action; see source. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fileId","in":"path","required":true,"description":"Opaque route segment accepted by this action; see source.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"user_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"user_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_FILEID:?PATH_FILEID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/user/list/files/'"${PATH_FILEID}"'/download' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/list/files/{fileId}/download"; path = path.replaceAll("{fileId}", encodeURIComponent(required("PATH_FILEID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/list/files/{fileId}/download" path = path.replace("{fileId}", urllib.parse.quote(os.environ["PATH_FILEID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_user_list_files_fileId_download","summary":"Kundendatei herunterladen","description":"Lädt die Datei mit fileId als direktem Dateidownload; die öffentliche files-ID muss zum ausgewählten Konto gehören. Gelöschte Dateien und Custom-Exportdateien sind ausgeschlossen. Ein nicht vorhandener Eintrag oder fehlende Dateibytes führt zu file_not_found.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fileId","in":"path","required":true,"description":"Opaque route segment accepted by this action; see source.","schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"user_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"user_012345abcdef_123456"}]}}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Dateidownload mit Dateinamen in Content-Disposition; der Dateityp hängt von der hochgeladenen Datei ab. Fehlende oder ausgeschlossene Dateien liefern eine JSON-Fehlerantwort.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"getUser","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/user/list/files/list Kundendateien und Dateimetadaten lesen Listet nicht gelöschte Dateien mit resource_type=user; Custom-Exportdateien sind ausgeschlossen. Der Pfadwert id in /user/{id}/files wird als Datei-ID (files_...) gefiltert, nicht als Eigentümer-ID. Für die Dateien eines Kunden /user/list/files/list mit filter auf resource_id und der user-ID verwenden. fields, order, limit und response_type gelten wie bei allgemeinen Sammlungen. - Operation-ID: get_v2_user_list_files_list - Dokumentation: /docs/api/v2/operations/get-v2-user-list-files-list-78a2b2831800/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | Für Dateien eines Kunden auf resource_id mit dessen öffentlicher user-ID filtern; resource_type=user wird serverseitig ergänzt. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Für Dateien eines Kunden auf resource_id mit dessen öffentlicher user-ID filtern; resource_type=user wird serverseitig ergänzt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"resource_id","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/user/list/files/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/list/files/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/list/files/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_user_list_files_list","summary":"Kundendateien und Dateimetadaten lesen","description":"Listet nicht gelöschte Dateien mit resource_type=user; Custom-Exportdateien sind ausgeschlossen. Der Pfadwert id in /user/{id}/files wird als Datei-ID (files_...) gefiltert, nicht als Eigentümer-ID. Für die Dateien eines Kunden /user/list/files/list mit filter auf resource_id und der user-ID verwenden. fields, order, limit und response_type gelten wie bei allgemeinen Sammlungen.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"Für Dateien eines Kunden auf resource_id mit dessen öffentlicher user-ID filtern; resource_type=user wird serverseitig ergänzt.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"resource_id","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmässig eine nach files-ID indizierte Sammlung; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"files_012345abcdef_123456":{"id":"files_012345abcdef_123456","filename":"Unterlagen.pdf","description":{"de":"Kursunterlagen"},"filetype":"avatar","resource_type":"user","resource_id":"user_012345abcdef_123456","last_change":"2026-10-22T10:00:00+02:00"}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"string"}},{"type":"array","items":{}}]},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"const":false}]},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"filename":{"type":"string"},"description":{"anyOf":[{"type":"object","properties":{},"additionalProperties":{"type":"string"}},{"type":"array","items":{}}]},"filetype":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"const":false}]},"last_change":{"type":"string","format":"date-time"}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Files.php","line":245},"reason":"Existing resource file query/readfile; the legacy File URL audit-log route is separate."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Files.php","method":"getFiles","line":245},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/user/list/I_want_demo_data Demo-Kunden für Entwicklung erzeugen Erzeugt nur bei ED_APP_ENVIRONMENT_SECURE=development synthetische Kunden und optional Buchungen für offer. number_users wird auf höchstens 50 begrenzt; waiting_list steuert die optionalen Buchungen. Auf dem Produktionsserver wird die Aktion mit only_development_environment abgelehnt. - Operation-ID: post_v2_user_list_I_want_demo_data - Dokumentation: /docs/api/v2/operations/post-v2-user-list-i-want-demo-data-0dd7053821b5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "number_users": 3 } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/user/list/I_want_demo_data' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/list/I_want_demo_data"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "number_users": 3 } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/list/I_want_demo_data" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"number_users\":3}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_user_list_I_want_demo_data","summary":"Demo-Kunden für Entwicklung erzeugen","description":"Erzeugt nur bei ED_APP_ENVIRONMENT_SECURE=development synthetische Kunden und optional Buchungen für offer. number_users wird auf höchstens 50 begrenzt; waiting_list steuert die optionalen Buchungen. Auf dem Produktionsserver wird die Aktion mit only_development_environment abgelehnt.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Nur in Entwicklung: user ist eine ID-Liste oder eine Liste von [user,booking]-Paaren; Produktion liefert einen Fehler.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"number_users":3,"user":["user_012345abcdef_123456","user_012345abcdef_123457","user_012345abcdef_123458"]}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"number_users":{"x-edoobox-canonicalType":"integer","description":"Vom Request übernommener und gegebenenfalls auf 50 begrenzter Wert."},"user":{"type":"array","items":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},{"type":"array","prefixItems":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["user_012345abcdef_123456"]},{"type":"string","pattern":"^booking_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["booking_012345abcdef_123456"]}],"minItems":2,"maxItems":2}]}}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"number_users":{"x-edoobox-canonicalType":"integer","description":"PHP int-Normalisierung; ein Integer ist die kanonische Eingabe, numerischer Text wird ebenfalls normalisiert. Der erlaubte Bereich wird nach der Umwandlung geprüft."},"offer":{"anyOf":[{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; synthetische Beispiele ersetzen.","examples":["offer_012345abcdef_123456"]},{"enum":[false,null,""]}]},"waiting_list":{"x-edoobox-canonicalType":"boolean","description":"Die native PHP-Wahrheitsauswertung normalisiert den Wert; true/false sind die kanonische JSON-Eingabe."}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Demo-Kunden für Entwicklung erzeugen","value":{"data":{"number_users":3}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"postUser","line":370,"branch":{"file":"application/models/v2/User.php","line":380}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/user/remove/files/{id} Datei als gelöscht markieren Markiert die Datei mit der öffentlichen files-ID als gelöscht; der Pfad enthält keine Kunden-ID. Custom-Exportdateien sind über diesen allgemeinen Dateihandler ausgeschlossen. - Operation-ID: delete_v2_user_remove_files_id - Dokumentation: /docs/api/v2/operations/delete-v2-user-remove-files-id-579871545d37/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche Datei-ID. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"description":"Öffentliche Datei-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/user/remove/files/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/remove/files/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/remove/files/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_user_remove_files_id","summary":"Datei als gelöscht markieren","description":"Markiert die Datei mit der öffentlichen files-ID als gelöscht; der Pfad enthält keine Kunden-ID. Custom-Exportdateien sind über diesen allgemeinen Dateihandler ausgeschlossen.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"description":"Öffentliche Datei-ID."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success sowie fachliche Statusfelder prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"delete":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Datei als gelöscht markieren","value":{}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Files.php","line":226,"reason":"Files explicitly rewrites the update/remove third segment to the file ID from segment5; exact Vue resource-file transport."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"deleteUser","line":465,"branch":{"file":"application/models/v2/User.php","line":470}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/user/update/files/{id} Dateimetadaten aktualisieren Ändert filename beziehungsweise die sprachabhängige description einer Datei. Die Pfad-ID ist eine öffentliche files-ID, keine user-ID. resource_type, resource_id und filetype sind unveränderlich; Dateiinhalt wird hier nicht hochgeladen. Custom-Exportdateien sind ausgeschlossen. - Operation-ID: put_v2_user_update_files_id - Dokumentation: /docs/api/v2/operations/put-v2-user-update-files-id-0ba6bc809bd4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | id | path | ja | Öffentliche Datei-ID; der Handler bearbeitet eine Datei, keinen Kunden. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"description":"Öffentliche Datei-ID; der Handler bearbeitet eine Datei, keinen Kunden."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": { "de": "Aktualisierte Kursunterlagen" } } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/user/update/files/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/update/files/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": { "de": "Aktualisierte Kursunterlagen" } } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/user/update/files/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":{\"de\":\"Aktualisierte Kursunterlagen\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_user_update_files_id","summary":"Dateimetadaten aktualisieren","description":"Ändert filename beziehungsweise die sprachabhängige description einer Datei. Die Pfad-ID ist eine öffentliche files-ID, keine user-ID. resource_type, resource_id und filetype sind unveränderlich; Dateiinhalt wird hier nicht hochgeladen. Custom-Exportdateien sind ausgeschlossen.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"description":"Öffentliche Datei-ID; der Handler bearbeitet eine Datei, keinen Kunden."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success sowie fachliche Statusfelder prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"update":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"filename":{"x-edoobox-canonicalType":"string","description":"aP-Normalisierung auf gespeicherten Text."},"description":{"type":"object","properties":{},"additionalProperties":{"x-edoobox-canonicalType":"string","description":"Text je unterstützter Sprache."},"description":"Sprachcode als Schlüssel, Text als Wert; nur unterstützte Sprachcodes werden gespeichert, normalisiert und auf 500 Zeichen begrenzt."}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Dateimetadaten aktualisieren","value":{"data":{"description":{"de":"Aktualisierte Kursunterlagen"}}}}}}}},"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Files.php","line":199,"reason":"Files explicitly rewrites the update/remove third segment to the file ID from segment5; exact Vue resource-file transport."},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"updateUser","line":419,"branch":{"file":"application/models/v2/User.php","line":424}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/user/upload/files Datei zu einer Ressource hochladen Lädt file als Multipartdatei hoch. meta ist ein JSON-Text mit dem data-Objekt, resource_type, resource_id und filetype; die Benutzerroute delegiert an den allgemeinen Dateihandler. resource_id muss zum angegebenen Ressourcentyp und Konto gehören. Avatar ersetzt ein vorhandenes Avatar und erlaubt JPEG/PNG/WebP bis 8 MiB; sign und image2/image3 erlauben Bilder bis 2 MiB; interne/externe Angebotsdateien bis 20 MiB. image1 wird vom Upload-Hook nicht akzeptiert. - Operation-ID: post_v2_user_upload_files - Dokumentation: /docs/api/v2/operations/post-v2-user-upload-files-3bf78515f3e5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${REQUEST_CONTENT_TYPE:?REQUEST_CONTENT_TYPE setzen}" curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/user/upload/files' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: '"${REQUEST_CONTENT_TYPE}" \ --data-binary '@request-body.bin' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. // Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import { readFileSync } from 'node:fs'; const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/user/upload/files"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = required('REQUEST_CONTENT_TYPE'); const body = readFileSync("request-body.bin"); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body selbst gemäss Definition vorbereiten; unbekannte Pflichtfelder werden nicht erfunden. # Bodydatei request-body.bin für multipart/form-data vorbereiten. REQUEST_CONTENT_TYPE einschliesslich nötiger Multipart-Boundary setzen. import os import urllib.parse import urllib.request import urllib.error from pathlib import Path path = "/v2/user/upload/files" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = os.environ["REQUEST_CONTENT_TYPE"] body = Path("request-body.bin").read_bytes() # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_user_upload_files","summary":"Datei zu einer Ressource hochladen","description":"Lädt file als Multipartdatei hoch. meta ist ein JSON-Text mit dem data-Objekt, resource_type, resource_id und filetype; die Benutzerroute delegiert an den allgemeinen Dateihandler. resource_id muss zum angegebenen Ressourcentyp und Konto gehören. Avatar ersetzt ein vorhandenes Avatar und erlaubt JPEG/PNG/WebP bis 8 MiB; sign und image2/image3 erlauben Bilder bis 2 MiB; interne/externe Angebotsdateien bis 20 MiB. image1 wird vom Upload-Hook nicht akzeptiert.","tags":["user"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Erfolgreiche Rückgabe in data; zusätzlich error und success sowie fachliche Statusfelder prüfen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"files_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^files_[a-f0-9]{12}_-?[0-9]+$","examples":["files_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},"insert":{"type":"boolean"}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"meta":{"type":"string","contentMediaType":"application/json","contentSchema":{"type":"object","properties":{"data":{"type":"object","properties":{"resource_type":{"type":"string","enum":["admin","user","offer"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"filetype":{"type":"string","enum":["avatar","sign","image2","image3","internalOfferFile","externalOfferFile"]},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":{"x-edoobox-canonicalType":"string","description":"Text je unterstützter Sprache."},"description":"Sprachcode als Schlüssel, Text als Wert; nur unterstützte Sprachcodes werden gespeichert, normalisiert und auf 500 Zeichen begrenzt."}},"additionalProperties":true,"required":["resource_type","resource_id","filetype"]}},"additionalProperties":true,"required":["data"]},"description":"JSON-Text mit vollständigem data-Metadatenobjekt; ersetzt im Dateihandler die Form-Metadaten."},"resource_type":{"type":"string","enum":["admin","user","offer"]},"resource_id":{"anyOf":[{"type":"string","pattern":"^admin_[a-f0-9]{12}_-?[0-9]+$","examples":["admin_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."},{"type":"string","pattern":"^offer_[a-f0-9]{12}_-?[0-9]+$","examples":["offer_012345abcdef_123456"],"description":"Öffentliche v2-ID aus dem ausgewählten Konto."}]},"filetype":{"type":"string","enum":["avatar","sign","image2","image3","internalOfferFile","externalOfferFile"]},"filename":{"type":"string"},"description":{"type":"object","properties":{},"additionalProperties":{"x-edoobox-canonicalType":"string","description":"Text je unterstützter Sprache."},"description":"Sprachcode als Schlüssel, Text als Wert; nur unterstützte Sprachcodes werden gespeichert, normalisiert und auf 500 Zeichen begrenzt."}},"additionalProperties":true,"required":["file"],"anyOf":[{"required":["meta"]},{"required":["resource_type","resource_id","filetype"]}]},"examples":{"avatar":{"summary":"Synthetische Metadaten; echte Datei-Bytes und Konto-ID verwenden.","value":{"file":"","meta":"{\"data\":{\"resource_type\":\"user\",\"resource_id\":\"user_012345abcdef_123456\",\"filetype\":\"avatar\",\"filename\":\"avatar.png\"}}"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/User.php","method":"postUser","line":1},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"user","short":"user","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/userduplicate/{id}/candidates Duplikatkandidaten eines Kunden prüfen Liest höchstens 50 offene Kandidaten für die öffentliche user-ID im Pfad. Fehlt deren Fingerprint, werden Index und Kandidaten dabei neu berechnet und gespeichert. user, config, limits und candidates stehen direkt im Antwortobjekt. - Operation-ID: get_v2_userduplicate_id_candidates - Dokumentation: /docs/api/v2/operations/get-v2-userduplicate-id-candidates-2c8597c263e5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Kunden-ID, keine userduplicate-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Kunden-ID, keine userduplicate-ID.","schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/userduplicate/'"${PATH_ID}"'/candidates' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/userduplicate/{id}/candidates"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/userduplicate/{id}/candidates" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_userduplicate_id_candidates","summary":"Duplikatkandidaten eines Kunden prüfen","description":"Liest höchstens 50 offene Kandidaten für die öffentliche user-ID im Pfad. Fehlt deren Fingerprint, werden Index und Kandidaten dabei neu berechnet und gespeichert. user, config, limits und candidates stehen direkt im Antwortobjekt.","tags":["userduplicate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Kunden-ID, keine userduplicate-ID.","schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Die Felder stehen direkt im JSON-Antwortobjekt; kein data-Umschlag.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"field":{"type":"string"},"message":{"type":"string"},"config":{"type":"object","properties":{"enabled":{"type":"boolean"},"candidate_threshold":{"type":"integer"},"auto_merge_threshold":{"type":["integer","null"]},"survivor_strategy":{"type":"string","enum":["newest","oldest"]},"max_merge_users":{"type":"integer"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"enabled":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer"}},"additionalProperties":true}},"rules_hash":{"type":"string"},"matching_rules_hash":{"type":"string"},"version":{"type":"integer"}},"additionalProperties":true},"limits":{"type":"object","properties":{"max_merge_users":{"type":"integer"}},"additionalProperties":true},"candidates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^duplicate_[a-f0-9]{24}$"},"score":{"type":"integer"},"required_match":{"type":"boolean"},"conflict_free":{"type":"boolean"},"auto_merge_eligible":{"type":"boolean"},"matched_fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"matched":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer"}},"additionalProperties":true}},"default_survivor":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"users":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"phone_number":{"type":"string"},"company":{"type":"string"}},"additionalProperties":true}},"fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"values":{"type":"object","additionalProperties":{}},"has_conflict":{"type":"boolean"}},"additionalProperties":true}},"impact":{"type":"object","additionalProperties":{"type":"object","properties":{"bookings":{"type":"integer"},"invoices":{"type":"integer"},"notes":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true}},"user":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"phone_number":{"type":"string"},"company":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true,"required":["error","success"]},"example":{"error":false,"success":true,"config":{"enabled":true,"candidate_threshold":60,"auto_merge_threshold":null,"survivor_strategy":"newest","max_merge_users":10,"rules":[{"field":"email","enabled":true,"required":false,"weight":2}],"rules_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","matching_rules_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","version":2},"limits":{"max_merge_users":10},"candidates":[],"user":{"id":"user_012345abcdef_123456","first_name":"Alex","last_name":"Beispiel","name":"Alex Beispiel","email":"alex@example.invalid","phone_number":"","company":""}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Userduplicate.php","line":25,"reason":"GET candidates calls getCandidatesForUser(...,false). Forced refresh, merge, ignore and clear are separate POST actions."},"reason":"Read duplicate candidates; create missing derived fingerprint, blocking-key and candidate index data first.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/20","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-get-effect":"maintenance","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/20","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Userduplicate.php","method":"getUserduplicate","line":12},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"userduplicate","short":"usdu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/userduplicate/{id}/refresh Duplikatsuche eines Kunden neu berechnen Berechnet Fingerprint und Kandidaten der öffentlichen user-ID im Pfad zwingend neu und liefert danach bis zu 50 offene Kandidaten. Der Request benötigt keine data-Felder; user, config, limits und candidates stehen direkt im Antwortobjekt. - Operation-ID: post_v2_userduplicate_id_refresh - Dokumentation: /docs/api/v2/operations/post-v2-userduplicate-id-refresh-8a6c0804caac/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | Öffentliche Kunden-ID, keine userduplicate-ID. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"Öffentliche Kunden-ID, keine userduplicate-ID.","schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/userduplicate/'"${PATH_ID}"'/refresh' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/userduplicate/{id}/refresh"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/userduplicate/{id}/refresh" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_userduplicate_id_refresh","summary":"Duplikatsuche eines Kunden neu berechnen","description":"Berechnet Fingerprint und Kandidaten der öffentlichen user-ID im Pfad zwingend neu und liefert danach bis zu 50 offene Kandidaten. Der Request benötigt keine data-Felder; user, config, limits und candidates stehen direkt im Antwortobjekt.","tags":["userduplicate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Öffentliche Kunden-ID, keine userduplicate-ID.","schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Die Felder stehen direkt im JSON-Antwortobjekt; kein data-Umschlag.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"field":{"type":"string"},"message":{"type":"string"},"config":{"type":"object","properties":{"enabled":{"type":"boolean"},"candidate_threshold":{"type":"integer"},"auto_merge_threshold":{"type":["integer","null"]},"survivor_strategy":{"type":"string","enum":["newest","oldest"]},"max_merge_users":{"type":"integer"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"enabled":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer"}},"additionalProperties":true}},"rules_hash":{"type":"string"},"matching_rules_hash":{"type":"string"},"version":{"type":"integer"}},"additionalProperties":true},"limits":{"type":"object","properties":{"max_merge_users":{"type":"integer"}},"additionalProperties":true},"candidates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^duplicate_[a-f0-9]{24}$"},"score":{"type":"integer"},"required_match":{"type":"boolean"},"conflict_free":{"type":"boolean"},"auto_merge_eligible":{"type":"boolean"},"matched_fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"matched":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer"}},"additionalProperties":true}},"default_survivor":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"users":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"phone_number":{"type":"string"},"company":{"type":"string"}},"additionalProperties":true}},"fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"values":{"type":"object","additionalProperties":{}},"has_conflict":{"type":"boolean"}},"additionalProperties":true}},"impact":{"type":"object","additionalProperties":{"type":"object","properties":{"bookings":{"type":"integer"},"invoices":{"type":"integer"},"notes":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true}},"user":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"phone_number":{"type":"string"},"company":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true,"required":["error","success"]},"example":{"error":false,"success":true,"config":{"enabled":true,"candidate_threshold":60,"auto_merge_threshold":null,"survivor_strategy":"newest","max_merge_users":10,"rules":[{"field":"email","enabled":true,"required":false,"weight":2}],"rules_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","matching_rules_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","version":2},"limits":{"max_merge_users":10},"candidates":[],"user":{"id":"user_012345abcdef_123456","first_name":"Alex","last_name":"Beispiel","name":"Alex Beispiel","email":"alex@example.invalid","phone_number":"","company":""}}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Duplikatsuche eines Kunden neu berechnen","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Userduplicate.php","method":"postUserduplicate","line":35},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"userduplicate","short":"usdu","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/userduplicate/clear Gefundene Duplikate und Suchindex leeren Entfernt offene und veraltete Kandidaten sowie Fingerprints und Blocking-Keys des Kontos und setzt den Suchstand zurück. Ignorierte Paare bleiben erhalten. Es sind keine data-Felder nötig; die Löschanzahlen stehen direkt im Antwortobjekt. - Operation-ID: post_v2_userduplicate_clear - Dokumentation: /docs/api/v2/operations/post-v2-userduplicate-clear-d3195e1363dc/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' {} EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/userduplicate/clear' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/userduplicate/clear"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({}); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/userduplicate/clear" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_userduplicate_clear","summary":"Gefundene Duplikate und Suchindex leeren","description":"Entfernt offene und veraltete Kandidaten sowie Fingerprints und Blocking-Keys des Kontos und setzt den Suchstand zurück. Ignorierte Paare bleiben erhalten. Es sind keine data-Felder nötig; die Löschanzahlen stehen direkt im Antwortobjekt.","tags":["userduplicate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Die Felder stehen direkt im JSON-Antwortobjekt; kein data-Umschlag.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"field":{"type":"string"},"message":{"type":"string"},"deleted_candidates":{"type":"integer"},"deleted_fingerprints":{"type":"integer"},"deleted_blocking_keys":{"type":"integer"}},"additionalProperties":true,"required":["error","success"]},"example":{"error":false,"success":true,"deleted_candidates":0,"deleted_fingerprints":0,"deleted_blocking_keys":0}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true},"examples":{"request":{"summary":"Gefundene Duplikate und Suchindex leeren","value":{}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Userduplicate.php","method":"postUserduplicate","line":35},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"userduplicate","short":"usdu","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/userduplicate/ignore Kundenduplikat-Paar dauerhaft ignorieren Markiert das Paar user_a/user_b als ignored oder erzeugt dafür einen Ignore-Eintrag. Beide öffentlichen user-IDs müssen unterschiedlich sein. ignored steht direkt im Antwortobjekt; der Handler verwendet keine interne Kandidaten-ID. - Operation-ID: post_v2_userduplicate_ignore - Dokumentation: /docs/api/v2/operations/post-v2-userduplicate-ignore-c0bcbafb85b6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "user_a": "user_012345abcdef_123456", "user_b": "user_012345abcdef_123457" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/userduplicate/ignore' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/userduplicate/ignore"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "user_a": "user_012345abcdef_123456", "user_b": "user_012345abcdef_123457" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/userduplicate/ignore" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"user_a\":\"user_012345abcdef_123456\",\"user_b\":\"user_012345abcdef_123457\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_userduplicate_ignore","summary":"Kundenduplikat-Paar dauerhaft ignorieren","description":"Markiert das Paar user_a/user_b als ignored oder erzeugt dafür einen Ignore-Eintrag. Beide öffentlichen user-IDs müssen unterschiedlich sein. ignored steht direkt im Antwortobjekt; der Handler verwendet keine interne Kandidaten-ID.","tags":["userduplicate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Die Felder stehen direkt im JSON-Antwortobjekt; kein data-Umschlag.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"field":{"type":"string"},"message":{"type":"string"},"ignored":{"type":"boolean"}},"additionalProperties":true,"required":["error","success"]},"example":{"error":false,"success":true,"ignored":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"user_a":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"user_b":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},"additionalProperties":true,"required":["user_a","user_b"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Kundenduplikat-Paar dauerhaft ignorieren","value":{"data":{"user_a":"user_012345abcdef_123456","user_b":"user_012345abcdef_123457"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Userduplicate.php","method":"postUserduplicate","line":35},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"userduplicate","short":"usdu","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/userduplicate/merge Geprüfte Kundenduplikate zusammenführen Führt source_users in survivor_user zusammen. Höchstens zehn Kunden insgesamt und das konfigurierte niedrigere Limit sind erlaubt; jedes Paar muss für die aktuelle Regelversion offen sein. field_values kann die gewählten Stammdaten setzen. Buchungs-, Rechnungs- und Notizbeziehungen werden übertragen, Quellkunden entfernt und der Zielindex erneuert. merged, survivor_user und source_users stehen direkt im Antwortobjekt. - Operation-ID: post_v2_userduplicate_merge - Dokumentation: /docs/api/v2/operations/post-v2-userduplicate-merge-38ec466ddb45/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "survivor_user": "user_012345abcdef_123456", "source_users": [ "user_012345abcdef_123457" ], "field_values": [ { "field": "first_name", "source_user": "user_012345abcdef_123456" } ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/userduplicate/merge' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/userduplicate/merge"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "survivor_user": "user_012345abcdef_123456", "source_users": [ "user_012345abcdef_123457" ], "field_values": [ { "field": "first_name", "source_user": "user_012345abcdef_123456" } ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/userduplicate/merge" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"survivor_user\":\"user_012345abcdef_123456\",\"source_users\":[\"user_012345abcdef_123457\"],\"field_values\":[{\"field\":\"first_name\",\"source_user\":\"user_012345abcdef_123456\"}]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_userduplicate_merge","summary":"Geprüfte Kundenduplikate zusammenführen","description":"Führt source_users in survivor_user zusammen. Höchstens zehn Kunden insgesamt und das konfigurierte niedrigere Limit sind erlaubt; jedes Paar muss für die aktuelle Regelversion offen sein. field_values kann die gewählten Stammdaten setzen. Buchungs-, Rechnungs- und Notizbeziehungen werden übertragen, Quellkunden entfernt und der Zielindex erneuert. merged, survivor_user und source_users stehen direkt im Antwortobjekt.","tags":["userduplicate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Die Felder stehen direkt im JSON-Antwortobjekt; kein data-Umschlag.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"field":{"type":"string"},"message":{"type":"string"},"merged":{"type":"boolean"},"survivor_user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"source_users":{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}}},"additionalProperties":true,"required":["error","success"]},"example":{"error":false,"success":true,"merged":true,"survivor_user":"user_012345abcdef_123456","source_users":["user_012345abcdef_123457"]}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"survivor_user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"source_users":{"anyOf":[{"type":"array","items":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"minItems":1,"maxItems":9},{"type":"object","minProperties":1,"maxProperties":9,"additionalProperties":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}},{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]}],"description":"Kanonisch Liste von Quellkunden; nativer (array)-Cast akzeptiert auch einen einzelnen öffentlichen ID-Text. Duplikate werden entfernt."},"field_values":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"source_user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"value":{"description":"Expliziter Feldwert für Text-/Kontaktfelder; ohne source_user wird dieser Wert übernommen. Für country eine vorhandene Quelle mit source_user wählen, keine rohe Länder-DB-ID senden."}},"additionalProperties":true,"required":["field"],"anyOf":[{"required":["source_user"]},{"required":["value"]}],"allOf":[{"if":{"properties":{"field":{"const":"country"}},"required":["field"]},"then":{"required":["source_user"]}}]}},{"type":"object","additionalProperties":{"type":"object","properties":{"field":{"type":"string"},"source_user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"value":{"description":"Expliziter Feldwert für Text-/Kontaktfelder; ohne source_user wird dieser Wert übernommen. Für country eine vorhandene Quelle mit source_user wählen, keine rohe Länder-DB-ID senden."}},"additionalProperties":true,"required":["field"],"anyOf":[{"required":["source_user"]},{"required":["value"]}],"allOf":[{"if":{"properties":{"field":{"const":"country"}},"required":["field"]},"then":{"required":["source_user"]}}]}}]}},"additionalProperties":true,"required":["survivor_user","source_users"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Geprüfte Kundenduplikate zusammenführen","value":{"data":{"survivor_user":"user_012345abcdef_123456","source_users":["user_012345abcdef_123457"],"field_values":[{"field":"first_name","source_user":"user_012345abcdef_123456"}]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Userduplicate.php","method":"postUserduplicate","line":35},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"userduplicate","short":"usdu","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/userduplicate/open Offene Kundenduplikate zur Prüfung laden Liest höchstens 200 offene Kandidatenpaare passend zur aktuellen Suchkonfiguration, nach Trefferwert und Aktualität sortiert. candidates enthält Kunden, Feldvergleiche und Auswirkungen; total ist die Anzahl der zurückgegebenen Paare, keine unbegrenzte Gesamtanzahl. Die Rückgabe steht direkt im Antwortobjekt. - Operation-ID: get_v2_userduplicate_open - Dokumentation: /docs/api/v2/operations/get-v2-userduplicate-open-a999e229ae25/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/userduplicate/open' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/userduplicate/open"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/userduplicate/open" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_userduplicate_open","summary":"Offene Kundenduplikate zur Prüfung laden","description":"Liest höchstens 200 offene Kandidatenpaare passend zur aktuellen Suchkonfiguration, nach Trefferwert und Aktualität sortiert. candidates enthält Kunden, Feldvergleiche und Auswirkungen; total ist die Anzahl der zurückgegebenen Paare, keine unbegrenzte Gesamtanzahl. Die Rückgabe steht direkt im Antwortobjekt.","tags":["userduplicate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Die Felder stehen direkt im JSON-Antwortobjekt; kein data-Umschlag.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"field":{"type":"string"},"message":{"type":"string"},"config":{"type":"object","properties":{"enabled":{"type":"boolean"},"candidate_threshold":{"type":"integer"},"auto_merge_threshold":{"type":["integer","null"]},"survivor_strategy":{"type":"string","enum":["newest","oldest"]},"max_merge_users":{"type":"integer"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"enabled":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer"}},"additionalProperties":true}},"rules_hash":{"type":"string"},"matching_rules_hash":{"type":"string"},"version":{"type":"integer"}},"additionalProperties":true},"limits":{"type":"object","properties":{"max_merge_users":{"type":"integer"}},"additionalProperties":true},"candidates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^duplicate_[a-f0-9]{24}$"},"score":{"type":"integer"},"required_match":{"type":"boolean"},"conflict_free":{"type":"boolean"},"auto_merge_eligible":{"type":"boolean"},"matched_fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"matched":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer"}},"additionalProperties":true}},"default_survivor":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"users":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"phone_number":{"type":"string"},"company":{"type":"string"}},"additionalProperties":true}},"fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"values":{"type":"object","additionalProperties":{}},"has_conflict":{"type":"boolean"}},"additionalProperties":true}},"impact":{"type":"object","additionalProperties":{"type":"object","properties":{"bookings":{"type":"integer"},"invoices":{"type":"integer"},"notes":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true}},"total":{"type":"integer"}},"additionalProperties":true,"required":["error","success"]},"example":{"error":false,"success":true,"config":{"enabled":true,"candidate_threshold":60,"auto_merge_threshold":null,"survivor_strategy":"newest","max_merge_users":10,"rules":[{"field":"email","enabled":true,"required":false,"weight":2}],"rules_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","matching_rules_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","version":2},"limits":{"max_merge_users":10},"candidates":[],"total":0}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Userduplicatefunc.php","line":102},"reason":"Read duplicate status/configuration or already indexed candidates."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Userduplicate.php","method":"getUserduplicate","line":12},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"userduplicate","short":"usdu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/userduplicate/status Stand der Kundenduplikatsuche lesen Liest Suchkonfiguration, letzten Suchstand, Sperrfrist, Statistik und Zusammenführungslimit. config, state und limits stehen direkt im Antwortobjekt; es gibt für diese Ressource keinen data-Umschlag. - Operation-ID: get_v2_userduplicate_status - Dokumentation: /docs/api/v2/operations/get-v2-userduplicate-status-89a541a6b17c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/userduplicate/status' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/userduplicate/status"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/userduplicate/status" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_userduplicate_status","summary":"Stand der Kundenduplikatsuche lesen","description":"Liest Suchkonfiguration, letzten Suchstand, Sperrfrist, Statistik und Zusammenführungslimit. config, state und limits stehen direkt im Antwortobjekt; es gibt für diese Ressource keinen data-Umschlag.","tags":["userduplicate"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Die Felder stehen direkt im JSON-Antwortobjekt; kein data-Umschlag.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"type":{"type":"string"},"field":{"type":"string"},"message":{"type":"string"},"config":{"type":"object","properties":{"enabled":{"type":"boolean"},"candidate_threshold":{"type":"integer"},"auto_merge_threshold":{"type":["integer","null"]},"survivor_strategy":{"type":"string","enum":["newest","oldest"]},"max_merge_users":{"type":"integer"},"rules":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"enabled":{"type":"boolean"},"required":{"type":"boolean"},"weight":{"type":"integer"}},"additionalProperties":true}},"rules_hash":{"type":"string"},"matching_rules_hash":{"type":"string"},"version":{"type":"integer"}},"additionalProperties":true},"limits":{"type":"object","properties":{"max_merge_users":{"type":"integer"}},"additionalProperties":true},"state":{"type":"object","properties":{"last_scan_at":{"type":"integer"},"last_cursor_user":{"anyOf":[{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","examples":["user_012345abcdef_123456"]},{"type":"null"}]},"locked_until":{"type":"integer"},"status":{"type":"string"},"stats":{"type":["object","array"],"additionalProperties":true,"items":{}}},"additionalProperties":true}},"additionalProperties":true,"required":["error","success"]},"example":{"error":false,"success":true,"config":{"enabled":true,"candidate_threshold":60,"auto_merge_threshold":null,"survivor_strategy":"newest","max_merge_users":10,"rules":[{"field":"email","enabled":true,"required":false,"weight":2}],"rules_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","matching_rules_hash":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","version":2},"limits":{"max_merge_users":10},"state":{"last_scan_at":0,"last_cursor_user":null,"locked_until":0,"status":"idle","stats":[]}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Userduplicatefunc.php","line":102},"reason":"Read duplicate status/configuration or already indexed candidates."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/02-booking-users.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Userduplicate.php","method":"getUserduplicate","line":12},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"userduplicate","short":"usdu","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/usermembership Mitgliedschaften eines Kontos suchen Liest konto-eigene Mitgliedschaften, optional nach Benutzer, Plan, Status, Quelle oder Nutzbarkeit gefiltert. Reihenfolge created_at und ID absteigend; total vor Pagination. id als Query/Filter wählt stattdessen die Detailansicht einschließlich Perioden, Kaufsnapshot und Journalbilanz. Vor der Ausgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Dadurch können Status, Gültigkeit, Nutzungsbestand und Zahlungsreferenzen sowie Audit/Outbox dauerhaft aktualisiert werden. - Operation-ID: get_v2_usermembership - Dokumentation: /docs/api/v2/operations/get-v2-usermembership-f76ba4fb4018/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert. | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0. | | id | query | nein | Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id. | | user | query | nein | Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user. | | plan | query | nein | Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["usermembership_012345abcdef_123456"]},"description":"Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id."},{"name":"user","in":"query","required":false,"schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user."},{"name":"plan","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/usermembership' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_usermembership","summary":"Mitgliedschaften eines Kontos suchen","description":"Liest konto-eigene Mitgliedschaften, optional nach Benutzer, Plan, Status, Quelle oder Nutzbarkeit gefiltert. Reihenfolge created_at und ID absteigend; total vor Pagination. id als Query/Filter wählt stattdessen die Detailansicht einschließlich Perioden, Kaufsnapshot und Journalbilanz. Vor der Ausgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Dadurch können Status, Gültigkeit, Nutzungsbestand und Zahlungsreferenzen sowie Audit/Outbox dauerhaft aktualisiert werden.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["usermembership_012345abcdef_123456"]},"description":"Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id."},{"name":"user","in":"query","required":false,"schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user."},{"name":"plan","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"usermembership_012345abcdef_123456","version":1,"user":"user_012345abcdef_123456","user_information":{"name":"Alex Beispiel","email":"alex@example.com"},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at"]}},{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"getUsermembership","line":24},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/usermembership Mitgliedschaft für einen Benutzer kaufen Kauft einen veröffentlichten oder intern zuweisbaren Plan für einen Benutzer des Kontos (source=admin_purchase). Erzeugt Mitgliedschaft, erste Periode und bei kostenpflichtigem Plan Buchhaltung/Rechnung mit pending_payment; kostenlose Käufe werden aktiviert oder geplant. Kein kostenloses Grant-API. idempotency_key verhindert doppelte Käufe desselben Ziels. Kontofähigkeit canPurchase, Planverfügbarkeit, Steuer und Parallelregel werden geprüft. Antwort enthält die vollständige Mitgliedschaft. Vor der Ausgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Dadurch können Status, Gültigkeit, Nutzungsbestand und Zahlungsreferenzen sowie Audit/Outbox dauerhaft aktualisiert werden. - Operation-ID: post_v2_usermembership - Dokumentation: /docs/api/v2/operations/post-v2-usermembership-5979ce16c533/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "user": "user_012345abcdef_123456", "plan": "membershipplan_012345abcdef_123456", "idempotency_key": "demo-membership-purchase-001", "reason": "Telefonische Bestellung" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/usermembership' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "user": "user_012345abcdef_123456", "plan": "membershipplan_012345abcdef_123456", "idempotency_key": "demo-membership-purchase-001", "reason": "Telefonische Bestellung" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"user\":\"user_012345abcdef_123456\",\"plan\":\"membershipplan_012345abcdef_123456\",\"idempotency_key\":\"demo-membership-purchase-001\",\"reason\":\"Telefonische Bestellung\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_usermembership","summary":"Mitgliedschaft für einen Benutzer kaufen","description":"Kauft einen veröffentlichten oder intern zuweisbaren Plan für einen Benutzer des Kontos (source=admin_purchase). Erzeugt Mitgliedschaft, erste Periode und bei kostenpflichtigem Plan Buchhaltung/Rechnung mit pending_payment; kostenlose Käufe werden aktiviert oder geplant. Kein kostenloses Grant-API. idempotency_key verhindert doppelte Käufe desselben Ziels. Kontofähigkeit canPurchase, Planverfügbarkeit, Steuer und Parallelregel werden geprüft. Antwort enthält die vollständige Mitgliedschaft. Vor der Ausgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Dadurch können Status, Gültigkeit, Nutzungsbestand und Zahlungsreferenzen sowie Audit/Outbox dauerhaft aktualisiert werden.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":1,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"pending_payment","active":false,"valid_from":null,"valid_until":null,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":null,"period_end":null,"payment_status":"pending","amounts":{"gross":"120.00","net":"111.01","tax":"8.99","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":"transaction_012345abcdef_123456","invoice":"invoice_012345abcdef_123456","invoice_number":"DEMO-2026-001","payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"idempotency_key":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."},"activation_at":{"type":["integer","number","string","boolean"],"description":"Optionaler Unix-Zeitpunkt für Kauf/Aktivierung; Standard aktuelle Zeit, mindestens 1."},"reason":{"type":"string"}},"additionalProperties":true,"required":["user","plan","idempotency_key"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mitgliedschaft für einen Benutzer kaufen","value":{"data":{"user":"user_012345abcdef_123456","plan":"membershipplan_012345abcdef_123456","idempotency_key":"demo-membership-purchase-001","reason":"Telefonische Bestellung"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"postUsermembership","line":117},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/usermembership Administrative Mitgliedschaftsaktion ausführen Führt data.action cancel, renewal, adjust-validity, credit-uses oder payment-link aus. Ohne unterstützte action folgt request_method_not_supported. Pflichtfelder je Aktion entsprechen den benannten Unterrouten; keine allgemeine Feldaktualisierung. Versionierte Änderungen verlangen den zuletzt gelesenen Stand, neue Kaufzuweisung erfolgt per POST. payment-link liefert statt Mitgliedschaft ein Linkobjekt. Auf der Collection-Route ist id als oberstes Feld des JSON-Envelopes erforderlich; PUT übernimmt keine Querydaten. - Operation-ID: put_v2_usermembership - Dokumentation: /docs/api/v2/operations/put-v2-usermembership-10ae5ffc83f4/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "action": "cancel", "version": 1, "mode": "period_end", "reason": "Kündigung durch Kunde" }, "id": "usermembership_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/usermembership' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "action": "cancel", "version": 1, "mode": "period_end", "reason": "Kündigung durch Kunde" }, "id": "usermembership_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"action\":\"cancel\",\"version\":1,\"mode\":\"period_end\",\"reason\":\"Kündigung durch Kunde\"},\"id\":\"usermembership_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_usermembership","summary":"Administrative Mitgliedschaftsaktion ausführen","description":"Führt data.action cancel, renewal, adjust-validity, credit-uses oder payment-link aus. Ohne unterstützte action folgt request_method_not_supported. Pflichtfelder je Aktion entsprechen den benannten Unterrouten; keine allgemeine Feldaktualisierung. Versionierte Änderungen verlangen den zuletzt gelesenen Stand, neue Kaufzuweisung erfolgt per POST. payment-link liefert statt Mitgliedschaft ein Linkobjekt. Auf der Collection-Route ist id als oberstes Feld des JSON-Envelopes erforderlich; PUT übernimmt keine Querydaten.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":1,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},{"type":"object","properties":{"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"url":{"type":"string","format":"uri"},"expires_at":{"type":"integer"}},"additionalProperties":true,"required":["period","invoice","url","expires_at"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"mode":{"type":"string","enum":["period_end","immediate"],"default":"period_end"},"reason":{"type":"string","minLength":1,"description":"Nichtleerer, revisionssicher protokollierter Kündigungsgrund."},"request_id":{"type":"string"},"action":{"const":"cancel"}},"additionalProperties":true,"required":["action","version","reason"]},{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"enabled":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"reason":{"type":"string","minLength":1},"request_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."},"action":{"const":"renewal"}},"additionalProperties":true,"required":["action","version","reason","request_id"]},{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"valid_until":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string","minLength":1},"request_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."},"action":{"const":"adjust-validity"}},"additionalProperties":true,"required":["action","version","valid_until","reason","request_id"]},{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"quantity":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string","minLength":1},"idempotency_key":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."},"action":{"const":"credit-uses"}},"additionalProperties":true,"required":["action","version","quantity","reason","idempotency_key"]},{"type":"object","properties":{"action":{"const":"payment-link"},"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"language":{"type":"string"}},"additionalProperties":true,"required":["action","period"]}]},"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Administrative Mitgliedschaftsaktion ausführen","value":{"data":{"action":"cancel","version":1,"mode":"period_end","reason":"Kündigung durch Kunde"},"id":"usermembership_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"updateUsermembership","line":194},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/usermembership/{id} Mitgliedschaft mit Perioden und Kontingent lesen Liest genau eine Mitgliedschaft mit aktuellem Status, Erneuerungseinstellungen, gespeichertem Kaufsnapshot, bis zu 500 Perioden und Journalbilanz. Vor der Ausgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Dadurch können Status, Gültigkeit, Nutzungsbestand und Zahlungsreferenzen sowie Audit/Outbox dauerhaft aktualisiert werden. - Operation-ID: get_v2_usermembership_id - Dokumentation: /docs/api/v2/operations/get-v2-usermembership-id-b2edf8b8b55e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_usermembership_id","summary":"Mitgliedschaft mit Perioden und Kontingent lesen","description":"Liest genau eine Mitgliedschaft mit aktuellem Status, Erneuerungseinstellungen, gespeichertem Kaufsnapshot, bis zu 500 Perioden und Journalbilanz. Vor der Ausgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Dadurch können Status, Gültigkeit, Nutzungsbestand und Zahlungsreferenzen sowie Audit/Outbox dauerhaft aktualisiert werden.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":1,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/22","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Usermembership.php","line":66,"reason":"Exact invoice branch supplies fixed action=invoice; item/list branches invoke their specific entitlement read service. Tenant public IDs are decoded before dispatch."},"reason":"Read one existing membership after automatic existing-ledger reconciliation; details include periods/ledger summary.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/22","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"getUsermembership","line":24},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/usermembership/{id} Administrative Mitgliedschaftsaktion ausführen Führt data.action cancel, renewal, adjust-validity, credit-uses oder payment-link aus. Ohne unterstützte action folgt request_method_not_supported. Pflichtfelder je Aktion entsprechen den benannten Unterrouten; keine allgemeine Feldaktualisierung. Versionierte Änderungen verlangen den zuletzt gelesenen Stand, neue Kaufzuweisung erfolgt per POST. payment-link liefert statt Mitgliedschaft ein Linkobjekt. - Operation-ID: put_v2_usermembership_id - Dokumentation: /docs/api/v2/operations/put-v2-usermembership-id-3c356521863b/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "action": "cancel", "version": 1, "mode": "period_end", "reason": "Kündigung durch Kunde" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "action": "cancel", "version": 1, "mode": "period_end", "reason": "Kündigung durch Kunde" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"action\":\"cancel\",\"version\":1,\"mode\":\"period_end\",\"reason\":\"Kündigung durch Kunde\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_usermembership_id","summary":"Administrative Mitgliedschaftsaktion ausführen","description":"Führt data.action cancel, renewal, adjust-validity, credit-uses oder payment-link aus. Ohne unterstützte action folgt request_method_not_supported. Pflichtfelder je Aktion entsprechen den benannten Unterrouten; keine allgemeine Feldaktualisierung. Versionierte Änderungen verlangen den zuletzt gelesenen Stand, neue Kaufzuweisung erfolgt per POST. payment-link liefert statt Mitgliedschaft ein Linkobjekt.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":1,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},{"type":"object","properties":{"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"url":{"type":"string","format":"uri"},"expires_at":{"type":"integer"}},"additionalProperties":true,"required":["period","invoice","url","expires_at"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"mode":{"type":"string","enum":["period_end","immediate"],"default":"period_end"},"reason":{"type":"string","minLength":1,"description":"Nichtleerer, revisionssicher protokollierter Kündigungsgrund."},"request_id":{"type":"string"},"action":{"const":"cancel"}},"additionalProperties":true,"required":["action","version","reason"]},{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"enabled":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"reason":{"type":"string","minLength":1},"request_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."},"action":{"const":"renewal"}},"additionalProperties":true,"required":["action","version","reason","request_id"]},{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"valid_until":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string","minLength":1},"request_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."},"action":{"const":"adjust-validity"}},"additionalProperties":true,"required":["action","version","valid_until","reason","request_id"]},{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"quantity":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string","minLength":1},"idempotency_key":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."},"action":{"const":"credit-uses"}},"additionalProperties":true,"required":["action","version","quantity","reason","idempotency_key"]},{"type":"object","properties":{"action":{"const":"payment-link"},"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"language":{"type":"string"}},"additionalProperties":true,"required":["action","period"]}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Administrative Mitgliedschaftsaktion ausführen","value":{"data":{"action":"cancel","version":1,"mode":"period_end","reason":"Kündigung durch Kunde"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"updateUsermembership","line":194},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/usermembership/{id}/adjust-validity Enddatum einer Mitgliedschaft korrigieren Setzt ein neues Enddatum (Unix-Sekunden) mit Pflichtgrund und stabilem request_id. Nur zeitbasierte Mitgliedschaften mit Status active/scheduled/expired und bezahlter/kostenloser aktueller Periode; Enddatum muss nach valid_from liegen. Bereits vorhandene Folgeperiode verhindert die Anpassung. canConfigure erforderlich. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie. - Operation-ID: post_v2_usermembership_id_adjust_validity - Dokumentation: /docs/api/v2/operations/post-v2-usermembership-id-adjust-validity-6b113b96cc69/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "valid_until": 1821536000, "reason": "Kulanzverlängerung", "request_id": "demo-validity-001" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/adjust-validity' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/adjust-validity"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "valid_until": 1821536000, "reason": "Kulanzverlängerung", "request_id": "demo-validity-001" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/adjust-validity" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"valid_until\":1821536000,\"reason\":\"Kulanzverlängerung\",\"request_id\":\"demo-validity-001\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_usermembership_id_adjust_validity","summary":"Enddatum einer Mitgliedschaft korrigieren","description":"Setzt ein neues Enddatum (Unix-Sekunden) mit Pflichtgrund und stabilem request_id. Nur zeitbasierte Mitgliedschaften mit Status active/scheduled/expired und bezahlter/kostenloser aktueller Periode; Enddatum muss nach valid_from liegen. Bereits vorhandene Folgeperiode verhindert die Anpassung. canConfigure erforderlich. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":2,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"valid_until":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string","minLength":1},"request_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."}},"additionalProperties":true,"required":["version","valid_until","reason","request_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Enddatum einer Mitgliedschaft korrigieren","value":{"data":{"version":1,"valid_until":1821536000,"reason":"Kulanzverlängerung","request_id":"demo-validity-001"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"postUsermembership","line":117},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/usermembership/{id}/adjust-validity Enddatum einer Mitgliedschaft korrigieren Setzt ein neues Enddatum (Unix-Sekunden) mit Pflichtgrund und stabilem request_id. Nur zeitbasierte Mitgliedschaften mit Status active/scheduled/expired und bezahlter/kostenloser aktueller Periode; Enddatum muss nach valid_from liegen. Bereits vorhandene Folgeperiode verhindert die Anpassung. canConfigure erforderlich. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie. - Operation-ID: put_v2_usermembership_id_adjust_validity - Dokumentation: /docs/api/v2/operations/put-v2-usermembership-id-adjust-validity-f4d52e881830/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "valid_until": 1821536000, "reason": "Kulanzverlängerung", "request_id": "demo-validity-001" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/adjust-validity' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/adjust-validity"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "valid_until": 1821536000, "reason": "Kulanzverlängerung", "request_id": "demo-validity-001" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/adjust-validity" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"valid_until\":1821536000,\"reason\":\"Kulanzverlängerung\",\"request_id\":\"demo-validity-001\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_usermembership_id_adjust_validity","summary":"Enddatum einer Mitgliedschaft korrigieren","description":"Setzt ein neues Enddatum (Unix-Sekunden) mit Pflichtgrund und stabilem request_id. Nur zeitbasierte Mitgliedschaften mit Status active/scheduled/expired und bezahlter/kostenloser aktueller Periode; Enddatum muss nach valid_from liegen. Bereits vorhandene Folgeperiode verhindert die Anpassung. canConfigure erforderlich. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":2,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"valid_until":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string","minLength":1},"request_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."}},"additionalProperties":true,"required":["version","valid_until","reason","request_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Enddatum einer Mitgliedschaft korrigieren","value":{"data":{"version":1,"valid_until":1821536000,"reason":"Kulanzverlängerung","request_id":"demo-validity-001"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"updateUsermembership","line":194},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/usermembership/{id}/cancel Mitgliedschaft kündigen Kündigt sofort oder zum Periodenende. period_end benötigt eine laufende automatische Verlängerung und ein Enddatum. Bereits bezahlte Folgeverlängerungen verhindern period_end; offene Verlängerungsrechnungen werden storniert. Eine sofortige Kündigung deaktiviert die Mitgliedschaft. Keine Rückerstattung allein durch diese Aktion. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie. - Operation-ID: put_v2_usermembership_id_cancel - Dokumentation: /docs/api/v2/operations/put-v2-usermembership-id-cancel-18d06ca1e608/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "mode": "period_end", "reason": "Kündigung durch Kunde" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/cancel' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/cancel"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "mode": "period_end", "reason": "Kündigung durch Kunde" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/cancel" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"mode\":\"period_end\",\"reason\":\"Kündigung durch Kunde\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_usermembership_id_cancel","summary":"Mitgliedschaft kündigen","description":"Kündigt sofort oder zum Periodenende. period_end benötigt eine laufende automatische Verlängerung und ein Enddatum. Bereits bezahlte Folgeverlängerungen verhindern period_end; offene Verlängerungsrechnungen werden storniert. Eine sofortige Kündigung deaktiviert die Mitgliedschaft. Keine Rückerstattung allein durch diese Aktion. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":2,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"mode":{"type":"string","enum":["period_end","immediate"],"default":"period_end"},"reason":{"type":"string","minLength":1,"description":"Nichtleerer, revisionssicher protokollierter Kündigungsgrund."},"request_id":{"type":"string"}},"additionalProperties":true,"required":["version","reason"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mitgliedschaft kündigen","value":{"data":{"version":1,"mode":"period_end","reason":"Kündigung durch Kunde"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"updateUsermembership","line":194},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/usermembership/{id}/credit-uses Nutzungskontingent gutschreiben Schreibt einer nutzungsbasierten Mitgliedschaft eine positive ganzzahlige Anzahl Nutzungen gut. Pflichtgrund und stabiler idempotency_key verhindern doppelte Gutschriften. Erhöht Gesamt-/Restkontingent und protokolliert Journal/Audit; exhausted wird wieder active. canConfigure erforderlich. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie. - Operation-ID: post_v2_usermembership_id_credit_uses - Dokumentation: /docs/api/v2/operations/post-v2-usermembership-id-credit-uses-d71f15aeada7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "quantity": 2, "reason": "Kulanzgutschrift", "idempotency_key": "demo-uses-credit-001" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/credit-uses' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/credit-uses"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "quantity": 2, "reason": "Kulanzgutschrift", "idempotency_key": "demo-uses-credit-001" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/credit-uses" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"quantity\":2,\"reason\":\"Kulanzgutschrift\",\"idempotency_key\":\"demo-uses-credit-001\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_usermembership_id_credit_uses","summary":"Nutzungskontingent gutschreiben","description":"Schreibt einer nutzungsbasierten Mitgliedschaft eine positive ganzzahlige Anzahl Nutzungen gut. Pflichtgrund und stabiler idempotency_key verhindern doppelte Gutschriften. Erhöht Gesamt-/Restkontingent und protokolliert Journal/Audit; exhausted wird wieder active. canConfigure erforderlich. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":2,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"quantity":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string","minLength":1},"idempotency_key":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."}},"additionalProperties":true,"required":["version","quantity","reason","idempotency_key"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Nutzungskontingent gutschreiben","value":{"data":{"version":1,"quantity":2,"reason":"Kulanzgutschrift","idempotency_key":"demo-uses-credit-001"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"postUsermembership","line":117},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/usermembership/{id}/credit-uses Nutzungskontingent gutschreiben Schreibt einer nutzungsbasierten Mitgliedschaft eine positive ganzzahlige Anzahl Nutzungen gut. Pflichtgrund und stabiler idempotency_key verhindern doppelte Gutschriften. Erhöht Gesamt-/Restkontingent und protokolliert Journal/Audit; exhausted wird wieder active. canConfigure erforderlich. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie. - Operation-ID: put_v2_usermembership_id_credit_uses - Dokumentation: /docs/api/v2/operations/put-v2-usermembership-id-credit-uses-373a2fdc3da6/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "quantity": 2, "reason": "Kulanzgutschrift", "idempotency_key": "demo-uses-credit-001" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/credit-uses' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/credit-uses"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "quantity": 2, "reason": "Kulanzgutschrift", "idempotency_key": "demo-uses-credit-001" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/credit-uses" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"quantity\":2,\"reason\":\"Kulanzgutschrift\",\"idempotency_key\":\"demo-uses-credit-001\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_usermembership_id_credit_uses","summary":"Nutzungskontingent gutschreiben","description":"Schreibt einer nutzungsbasierten Mitgliedschaft eine positive ganzzahlige Anzahl Nutzungen gut. Pflichtgrund und stabiler idempotency_key verhindern doppelte Gutschriften. Erhöht Gesamt-/Restkontingent und protokolliert Journal/Audit; exhausted wird wieder active. canConfigure erforderlich. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":2,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"quantity":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"reason":{"type":"string","minLength":1},"idempotency_key":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."}},"additionalProperties":true,"required":["version","quantity","reason","idempotency_key"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Nutzungskontingent gutschreiben","value":{"data":{"version":1,"quantity":2,"reason":"Kulanzgutschrift","idempotency_key":"demo-uses-credit-001"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"updateUsermembership","line":194},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/usermembership/{id}/invoice Rechnungslink einer Mitgliedschaft erzeugen Erzeugt einen signierten, sieben Tage gültigen Rechnungslink für genau eine eigene Periode. period ist als Query oder gleichnamiger filter-Eintrag erforderlich. Unterstützt pending/paid/failed/refunded/partially_refunded bei vorhandener Rechnung/Transaktion. Zahlungsabgleich vor der Erzeugung kann Mitgliedschafts- und Periodenzustände, Audit/Outbox aktualisieren. Der Link lädt kein PDF in dieser Antwort. - Operation-ID: get_v2_usermembership_id_invoice - Dokumentation: /docs/api/v2/operations/get-v2-usermembership-id-invoice-51e78f6ad535/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | Alternative zum Query-Parameter period: Periodenauswahl über property=period. | | language | query | nein | Sprache des Rechnungslinks; Fallback Kontosprache. | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | | period | query | nein | Erforderliche Periodenauswahl für den Rechnungslink, alternativ filter-Eintrag period. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"Alternative zum Query-Parameter period: Periodenauswahl über property=period.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"period","value":"membershipperiod_012345abcdef_123456"}]}}},{"name":"language","in":"query","required":false,"description":"Sprache des Rechnungslinks; Fallback Kontosprache.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"period","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Erforderliche Periodenauswahl für den Rechnungslink, alternativ filter-Eintrag period."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/invoice"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/invoice" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_usermembership_id_invoice","summary":"Rechnungslink einer Mitgliedschaft erzeugen","description":"Erzeugt einen signierten, sieben Tage gültigen Rechnungslink für genau eine eigene Periode. period ist als Query oder gleichnamiger filter-Eintrag erforderlich. Unterstützt pending/paid/failed/refunded/partially_refunded bei vorhandener Rechnung/Transaktion. Zahlungsabgleich vor der Erzeugung kann Mitgliedschafts- und Periodenzustände, Audit/Outbox aktualisieren. Der Link lädt kein PDF in dieser Antwort.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"Alternative zum Query-Parameter period: Periodenauswahl über property=period.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"period","value":"membershipperiod_012345abcdef_123456"}]}}},{"name":"language","in":"query","required":false,"description":"Sprache des Rechnungslinks; Fallback Kontosprache.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."},{"name":"period","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Erforderliche Periodenauswahl für den Rechnungslink, alternativ filter-Eintrag period."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"period":"membershipperiod_012345abcdef_123456","invoice":"invoice_012345abcdef_123456","url":"https://app.edoobox.com/my/memberships/usermembership_012345abcdef_123456/invoice?expires=1790604800&sig=synthetic-signature","expires_at":1790604800}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"url":{"type":"string","format":"uri"},"expires_at":{"type":"integer"}},"additionalProperties":true,"required":["period","invoice","url","expires_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Usermembership.php","line":49,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/23","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Usermembership.php","line":49,"reason":"Exact invoice branch supplies fixed action=invoice; item/list branches invoke their specific entitlement read service. Tenant public IDs are decoded before dispatch."},"reason":"Read a link to an already-existing period invoice, after automatic existing-ledger reconciliation.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/23","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["period"]},{"query":["filter"],"containsProperty":["period"]}],"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"getUsermembership","line":24,"branch":{"file":"application/models/v2/Usermembership.php","line":49}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/usermembership/{id}/payment-link Zahlungslink für eine Mitgliedschaft erzeugen Erzeugt einen 48 Stunden gültigen Link zur verschlüsselten Buchungs-Zahlungsroute für eine eigene Periode mit payment_status pending/failed und gültiger Transaktion. Die Antwort enthält keine Zahlungsausführung. Zahlungsabgleich vor der Linkausgabe kann Perioden-/Mitgliedschaftszustand und Audit/Outbox aktualisieren. Sprache stammt aus data.language, sonst aus dem Konto. - Operation-ID: post_v2_usermembership_id_payment_link - Dokumentation: /docs/api/v2/operations/post-v2-usermembership-id-payment-link-9b7b770668d5/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "period": "membershipperiod_012345abcdef_123456", "language": "de" } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/payment-link' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/payment-link"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "period": "membershipperiod_012345abcdef_123456", "language": "de" } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/payment-link" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"period\":\"membershipperiod_012345abcdef_123456\",\"language\":\"de\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_usermembership_id_payment_link","summary":"Zahlungslink für eine Mitgliedschaft erzeugen","description":"Erzeugt einen 48 Stunden gültigen Link zur verschlüsselten Buchungs-Zahlungsroute für eine eigene Periode mit payment_status pending/failed und gültiger Transaktion. Die Antwort enthält keine Zahlungsausführung. Zahlungsabgleich vor der Linkausgabe kann Perioden-/Mitgliedschaftszustand und Audit/Outbox aktualisieren. Sprache stammt aus data.language, sonst aus dem Konto.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"period":"membershipperiod_012345abcdef_123456","invoice":null,"url":"https://app.edoobox.com/booking/synthetic-encrypted-payment-route","expires_at":1790172800}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"url":{"type":"string","format":"uri"},"expires_at":{"type":"integer"}},"additionalProperties":true,"required":["period","invoice","url","expires_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"language":{"type":"string"}},"additionalProperties":true,"required":["period"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zahlungslink für eine Mitgliedschaft erzeugen","value":{"data":{"period":"membershipperiod_012345abcdef_123456","language":"de"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"postUsermembership","line":117,"branch":{"file":"application/models/v2/Usermembership.php","line":122}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/usermembership/{id}/payment-link Zahlungslink für eine Mitgliedschaft erzeugen Erzeugt einen 48 Stunden gültigen Link zur verschlüsselten Buchungs-Zahlungsroute für eine eigene Periode mit payment_status pending/failed und gültiger Transaktion. Die Antwort enthält keine Zahlungsausführung. Zahlungsabgleich vor der Linkausgabe kann Perioden-/Mitgliedschaftszustand und Audit/Outbox aktualisieren. Sprache stammt aus data.language, sonst aus dem Konto. - Operation-ID: put_v2_usermembership_id_payment_link - Dokumentation: /docs/api/v2/operations/put-v2-usermembership-id-payment-link-f88b768b83cb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "period": "membershipperiod_012345abcdef_123456", "language": "de" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/payment-link' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/payment-link"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "period": "membershipperiod_012345abcdef_123456", "language": "de" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/payment-link" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"period\":\"membershipperiod_012345abcdef_123456\",\"language\":\"de\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_usermembership_id_payment_link","summary":"Zahlungslink für eine Mitgliedschaft erzeugen","description":"Erzeugt einen 48 Stunden gültigen Link zur verschlüsselten Buchungs-Zahlungsroute für eine eigene Periode mit payment_status pending/failed und gültiger Transaktion. Die Antwort enthält keine Zahlungsausführung. Zahlungsabgleich vor der Linkausgabe kann Perioden-/Mitgliedschaftszustand und Audit/Outbox aktualisieren. Sprache stammt aus data.language, sonst aus dem Konto.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"period":"membershipperiod_012345abcdef_123456","invoice":null,"url":"https://app.edoobox.com/booking/synthetic-encrypted-payment-route","expires_at":1790172800}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"url":{"type":"string","format":"uri"},"expires_at":{"type":"integer"}},"additionalProperties":true,"required":["period","invoice","url","expires_at"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"language":{"type":"string"}},"additionalProperties":true,"required":["period"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Zahlungslink für eine Mitgliedschaft erzeugen","value":{"data":{"period":"membershipperiod_012345abcdef_123456","language":"de"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"updateUsermembership","line":194},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/usermembership/{id}/renewal Automatische Verlängerung einstellen Aktiviert/deaktiviert die automatische Verlängerung mit Pflichtgrund und stabilem request_id. enabled fehlt: false. Aktivieren erfordert zulässige aktive/geplante Mitgliedschaft, künftiges Enddatum, Verlängerungsfreigabe im Kaufsnapshot und canRunRenewal; bereits angelegte Folgeperiode verhindert Aktivierung. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie. - Operation-ID: put_v2_usermembership_id_renewal - Dokumentation: /docs/api/v2/operations/put-v2-usermembership-id-renewal-0df17534abeb/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "version": 1, "enabled": false, "reason": "Verlängerung deaktivieren", "request_id": "demo-renewal-001" } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/renewal' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/renewal"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "version": 1, "enabled": false, "reason": "Verlängerung deaktivieren", "request_id": "demo-renewal-001" } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/renewal" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"version\":1,\"enabled\":false,\"reason\":\"Verlängerung deaktivieren\",\"request_id\":\"demo-renewal-001\"}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_usermembership_id_renewal","summary":"Automatische Verlängerung einstellen","description":"Aktiviert/deaktiviert die automatische Verlängerung mit Pflichtgrund und stabilem request_id. enabled fehlt: false. Aktivieren erfordert zulässige aktive/geplante Mitgliedschaft, künftiges Enddatum, Verlängerungsfreigabe im Kaufsnapshot und canRunRenewal; bereits angelegte Folgeperiode verhindert Aktivierung. version ist die zuletzt gelesene Mitgliedschaftsversion; die Änderung erhöht sie.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"usermembership_012345abcdef_123456","version":2,"user":"user_012345abcdef_123456","user_information":{"name":"","email":""},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000,"plan_snapshot":{"name":"Jahresmitgliedschaft","validity":{"type":"relative_duration","from":null,"until":null,"value":1,"unit":"year","uses":null,"promo_code_redemption":false},"discount":{"type":"percent","value":"10.00","currency":null},"sale":{"amount":"120.00","currency":"CHF","vat":null}},"periods":[{"id":"membershipperiod_012345abcdef_123456","membership":"usermembership_012345abcdef_123456","type":"initial","period_start":1790000000,"period_end":1821536000,"payment_status":"free","amounts":{"gross":"0.00","net":"0.00","tax":"0.00","currency":"CHF"},"vat":{"id":null,"rate":"0.0000","label":""},"title":"Jahresmitgliedschaft","plan_version":1,"design":null,"transaction":null,"invoice":null,"invoice_number":null,"payment":null,"reminder_due_at":1818944000,"renewal_due_at":1821536000,"created_at":1790000000,"updated_at":1790000000}],"ledger_summary":{"balance":0,"events":0}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"version":{"type":["integer","number","string","boolean"],"description":"Wird serverseitig nach Integer konvertiert; für neue Clients eine ganze Zahl senden."},"enabled":{"type":["boolean","integer","string"],"description":"Normalisierung mit FILTER_VALIDATE_BOOL; z.B. true/false, 1/0 und \"true\"/\"false\"."},"reason":{"type":"string","minLength":1},"request_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:-]+$","description":"Clientseitig stabiler Schlüssel für genau diesen Vorgang; nach Whitespace-Trim 1–128 Zeichen."}},"additionalProperties":true,"required":["version","reason","request_id"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Automatische Verlängerung einstellen","value":{"data":{"version":1,"enabled":false,"reason":"Verlängerung deaktivieren","request_id":"demo-renewal-001"}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"updateUsermembership","line":194},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/usermembership/{id}/timeline Verlauf einer Mitgliedschaft lesen Liest unveränderliche Audit- und Journalereignisse einer Mitgliedschaft einschließlich ihrer Perioden. Doppelte Vergabe-/Gutschriftanzeigen werden zusammengeführt; technische IDs aus Änderungen werden entfernt. Pagination per limit, Sortierung Zeit absteigend; total vor Pagination. Diese Detailaktion führt keinen Zahlungsabgleich aus. - Operation-ID: get_v2_usermembership_id_timeline - Dokumentation: /docs/api/v2/operations/get-v2-usermembership-id-timeline-64b93054a31d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0. | | id | path | ja | Source-reviewed path context; normal dispatcher ownership and permission checks still apply. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/usermembership/'"${PATH_ID}"'/timeline' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/{id}/timeline"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/{id}/timeline" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_usermembership_id_timeline","summary":"Verlauf einer Mitgliedschaft lesen","description":"Liest unveränderliche Audit- und Journalereignisse einer Mitgliedschaft einschließlich ihrer Perioden. Doppelte Vergabe-/Gutschriftanzeigen werden zusammengeführt; technische IDs aus Änderungen werden entfernt. Pagination per limit, Sortierung Zeit absteigend; total vor Pagination. Diese Detailaktion führt keinen Zahlungsabgleich aus.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["usermembership_012345abcdef_123456"]},"description":"Source-reviewed path context; normal dispatcher ownership and permission checks still apply."}],"responses":{"200":{"description":"data ist eine chronologisch absteigend sortierte Ereignisliste; total enthält die Gesamtzahl vor Pagination. error=false und success=true.","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"type":"redemption","event_type":"membership.usage_consumed","occurred_at":1790000000,"reason":"Buchung","actor":{"type":"system"},"resource":{"type":"membershipredemption","id":"membershipredemption_012345abcdef_123456"},"changes":{"quantity_delta":-1}}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["audit","redemption"]},"event_type":{"type":"string"},"occurred_at":{"type":"integer"},"reason":{"type":"string"},"actor":{"type":"object","properties":{"type":{"type":"string"}},"additionalProperties":true,"required":["type"]},"resource":{"type":"object","properties":{"type":{"type":"string"},"id":{"type":"string","description":"Öffentliche v2-ID des im type genannten Objekts."}},"additionalProperties":true,"required":["type","id"]},"changes":{"type":"object","properties":{"before":{"type":["object","null"],"additionalProperties":true,"description":"Zugelassene Zustandsfelder und explizit in v2 umgewandelte Ressourcen; rohe technische IDs werden entfernt."},"after":{"type":["object","null"],"additionalProperties":true,"description":"Zugelassene Zustandsfelder und explizit in v2 umgewandelte Ressourcen; rohe technische IDs werden entfernt."},"quantity_delta":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true,"required":["type","event_type","occurred_at","reason","actor","resource","changes"]}},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-routeAliasEvidence":{"file":"application/models/v2/Usermembership.php","line":34,"reason":"Reviewed dispatcher alias: the same literal action/constructor Structure is selected; the third segment supplies ID/filter context or an ignored collection spelling. Domain ownership, required data and model validation remain unchanged."},"x-edoobox-get-effect":"read","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/24","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Usermembership.php","line":34,"reason":"Timeline branch directly calls timeline.list before the other invoice/item/list branches."},"reason":"Read existing membership audit and redemption events as a timeline; do not refresh payment state.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/24","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"getUsermembership","line":24,"branch":{"file":"application/models/v2/Usermembership.php","line":34}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/usermembership/list Mitgliedschaften eines Kontos suchen Liest konto-eigene Mitgliedschaften, optional nach Benutzer, Plan, Status, Quelle oder Nutzbarkeit gefiltert. Reihenfolge created_at und ID absteigend; total vor Pagination. id als Query/Filter wählt stattdessen die Detailansicht einschließlich Perioden, Kaufsnapshot und Journalbilanz. Vor der Ausgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Dadurch können Status, Gültigkeit, Nutzungsbestand und Zahlungsreferenzen sowie Audit/Outbox dauerhaft aktualisiert werden. - Operation-ID: get_v2_usermembership_list - Dokumentation: /docs/api/v2/operations/get-v2-usermembership-list-8137d816d8af/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert. | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0. | | id | query | nein | Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id. | | user | query | nein | Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user. | | plan | query | nein | Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["usermembership_012345abcdef_123456"]},"description":"Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id."},{"name":"user","in":"query","required":false,"schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user."},{"name":"plan","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/usermembership/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_usermembership_list","summary":"Mitgliedschaften eines Kontos suchen","description":"Liest konto-eigene Mitgliedschaften, optional nach Benutzer, Plan, Status, Quelle oder Nutzbarkeit gefiltert. Reihenfolge created_at und ID absteigend; total vor Pagination. id als Query/Filter wählt stattdessen die Detailansicht einschließlich Perioden, Kaufsnapshot und Journalbilanz. Vor der Ausgabe werden verpasste Zahlungsrückmeldungen abgeglichen. Dadurch können Status, Gültigkeit, Nutzungsbestand und Zahlungsreferenzen sowie Audit/Outbox dauerhaft aktualisiert werden.","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["usermembership_012345abcdef_123456"]},"description":"Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id."},{"name":"user","in":"query","required":false,"schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user."},{"name":"plan","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"id":"usermembership_012345abcdef_123456","version":1,"user":"user_012345abcdef_123456","user_information":{"name":"Alex Beispiel","email":"alex@example.com"},"plan":"membershipplan_012345abcdef_123456","plan_version":1,"name":"Jahresmitgliedschaft","status":"active","active":true,"valid_from":1790000000,"valid_until":1821536000,"granted_uses":null,"remaining_uses":null,"membership_promo_code":null,"source":"admin_purchase","renewal":{"auto_renew":false,"cancel_at_period_end":false,"cancel_requested_at":null,"cancel_effective_at":null,"next_renewal_at":null},"current_period":"membershipperiod_012345abcdef_123456","created_at":1790000000,"updated_at":1790000000}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at"]}},{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"},"plan_snapshot":{"type":["object","array"],"properties":{"name":{"type":"string"},"validity":{"type":"object","properties":{"type":{"type":"string","enum":["fixed_range","relative_duration","usage_count"]},"from":{"type":["integer","null"]},"until":{"type":["integer","null"]},"value":{"type":["integer","null"]},"unit":{"type":["string","null"]},"uses":{"type":["integer","null"]},"promo_code_redemption":{"type":"boolean"}},"additionalProperties":true},"discount":{"type":"object","properties":{"type":{"type":"string","enum":["percent","fixed"]},"value":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":["string","null"]}},"additionalProperties":true},"sale":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"},"vat":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."}},"additionalProperties":true},"renewal":{"type":"object","additionalProperties":true},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."}},"additionalProperties":true,"items":{},"description":"Gespeicherter, versionsabhängiger Kaufsnapshot. Enthält ggf. weitere öffentliche Kauf-/Rechtskonfiguration; interne vat_id/design_id werden in v2-Referenzen umgewandelt."},"periods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"membership":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"type":{"type":"string"},"period_start":{"type":["integer","null"]},"period_end":{"type":["integer","null"]},"payment_status":{"type":"string"},"amounts":{"type":"object","properties":{"gross":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"net":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"tax":{"type":"string","pattern":"^-?[0-9]+\\.[0-9]{2}$","description":"Dezimalbetrag als String mit zwei Nachkommastellen."},"currency":{"type":"string"}},"additionalProperties":true,"required":["gross","net","tax","currency"]},"vat":{"type":"object","properties":{"id":{"type":["string","null"],"pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"rate":{"type":"string"},"label":{"type":"string"}},"additionalProperties":true,"required":["id","rate","label"]},"title":{"type":"string"},"plan_version":{"type":"integer"},"design":{"type":["string","null"],"pattern":"^design_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"transaction":{"type":["string","null"],"pattern":"^transaction_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"invoice_number":{"type":["string","null"]},"payment":{"type":["string","null"],"pattern":"^pay_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine rohe Datenbank-ID."},"reminder_due_at":{"type":["integer","null"]},"renewal_due_at":{"type":["integer","null"]},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","membership","type","period_start","period_end","payment_status","amounts","vat","title","plan_version","design","transaction","invoice","invoice_number","payment","reminder_due_at","renewal_due_at","created_at","updated_at"]}},"ledger_summary":{"type":"object","properties":{"balance":{"type":"integer"},"events":{"type":"integer"}},"additionalProperties":true,"required":["balance","events"]}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at","plan_snapshot","periods","ledger_summary"]}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-get-permission-review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/21","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."},"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Usermembership.php","line":68,"reason":"Exact invoice branch supplies fixed action=invoice; item/list branches invoke their specific entitlement read service. Tenant public IDs are decoded before dispatch."},"reason":"List existing user memberships after automatic existing-ledger reconciliation, before status/active filters and pagination.","review":{"file":"docs/openapi/reviews/vue-get-permission-03.json","pointer":"/operations/21","sha256":"cd3a09695d376f9f3c02b6f01b4407cec6498ab07aded113fd0432c98805e20a","scope":"Resource-handler effect. Shared account lifecycle maintenance, read caches and technical revision fences remain separately authorized."}},"x-edoobox-readonly-maintenance":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"getUsermembership","line":24},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/usermembership/list/invoice Rechnungslink einer Mitgliedschaft erzeugen Erzeugt einen signierten, sieben Tage gültigen Rechnungslink für genau eine eigene Periode. period ist als Query oder gleichnamiger filter-Eintrag erforderlich. Unterstützt pending/paid/failed/refunded/partially_refunded bei vorhandener Rechnung/Transaktion. Zahlungsabgleich vor der Erzeugung kann Mitgliedschafts- und Periodenzustände, Audit/Outbox aktualisieren. Der Link lädt kein PDF in dieser Antwort. Auf /list wird diese Aktion nur mit id als direktem Query oder filter-Eintrag ausgeführt; sonst folgt die gewöhnliche Mitgliedschaftsliste (einschließlich Zahlungsabgleich). - Operation-ID: get_v2_usermembership_list_invoice - Dokumentation: /docs/api/v2/operations/get-v2-usermembership-list-invoice-3b28fa56ffcf/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert. | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0. | | language | query | nein | Sprache des Rechnungslinks; Fallback Kontosprache. | | period | query | nein | Erforderliche Periodenauswahl für den Rechnungslink, alternativ filter-Eintrag period. | | id | query | nein | Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id. | | user | query | nein | Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user. | | plan | query | nein | Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"query","required":false,"description":"Sprache des Rechnungslinks; Fallback Kontosprache.","schema":{"type":"string"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Erforderliche Periodenauswahl für den Rechnungslink, alternativ filter-Eintrag period."},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["usermembership_012345abcdef_123456"]},"description":"Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id."},{"name":"user","in":"query","required":false,"schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user."},{"name":"plan","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/usermembership/list/invoice' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/list/invoice"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/list/invoice" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_usermembership_list_invoice","summary":"Rechnungslink einer Mitgliedschaft erzeugen","description":"Erzeugt einen signierten, sieben Tage gültigen Rechnungslink für genau eine eigene Periode. period ist als Query oder gleichnamiger filter-Eintrag erforderlich. Unterstützt pending/paid/failed/refunded/partially_refunded bei vorhandener Rechnung/Transaktion. Zahlungsabgleich vor der Erzeugung kann Mitgliedschafts- und Periodenzustände, Audit/Outbox aktualisieren. Der Link lädt kein PDF in dieser Antwort. Auf /list wird diese Aktion nur mit id als direktem Query oder filter-Eintrag ausgeführt; sonst folgt die gewöhnliche Mitgliedschaftsliste (einschließlich Zahlungsabgleich).","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"language","in":"query","required":false,"description":"Sprache des Rechnungslinks; Fallback Kontosprache.","schema":{"type":"string"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Erforderliche Periodenauswahl für den Rechnungslink, alternativ filter-Eintrag period."},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["usermembership_012345abcdef_123456"]},"description":"Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id."},{"name":"user","in":"query","required":false,"schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user."},{"name":"plan","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":{"period":"membershipperiod_012345abcdef_123456","invoice":"invoice_012345abcdef_123456","url":"https://app.edoobox.com/my/memberships/usermembership_012345abcdef_123456/invoice?expires=1790604800&sig=synthetic-signature","expires_at":1790604800}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","properties":{"period":{"type":"string","pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"invoice":{"type":["string","null"],"pattern":"^invoice_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"url":{"type":"string","format":"uri"},"expires_at":{"type":"integer"}},"additionalProperties":true,"required":["period","invoice","url","expires_at"]},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at"]}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-parameterAlternatives":[{"query":["period"]},{"query":["filter"],"containsProperty":["period"]}],"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"getUsermembership","line":24,"branch":{"file":"application/models/v2/Usermembership.php","line":49}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/usermembership/list/timeline Verlauf einer Mitgliedschaft lesen Liest unveränderliche Audit- und Journalereignisse einer Mitgliedschaft einschließlich ihrer Perioden. Doppelte Vergabe-/Gutschriftanzeigen werden zusammengeführt; technische IDs aus Änderungen werden entfernt. Pagination per limit, Sortierung Zeit absteigend; total vor Pagination. Diese Detailaktion führt keinen Zahlungsabgleich aus. Auf /list wird diese Aktion nur mit id als direktem Query oder filter-Eintrag ausgeführt; sonst folgt die gewöhnliche Mitgliedschaftsliste (einschließlich Zahlungsabgleich). - Operation-ID: get_v2_usermembership_list_timeline - Dokumentation: /docs/api/v2/operations/get-v2-usermembership-list-timeline-547b0cb474ca/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | filter | query | nein | property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert. | | limit | query | nein | JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0. | | id | query | nein | Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id. | | user | query | nein | Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user. | | plan | query | nein | Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan. | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"filter","in":"query","required":false,"description":"property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["usermembership_012345abcdef_123456"]},"description":"Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id."},{"name":"user","in":"query","required":false,"schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user."},{"name":"plan","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/usermembership/list/timeline' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/usermembership/list/timeline"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/usermembership/list/timeline" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_usermembership_list_timeline","summary":"Verlauf einer Mitgliedschaft lesen","description":"Liest unveränderliche Audit- und Journalereignisse einer Mitgliedschaft einschließlich ihrer Perioden. Doppelte Vergabe-/Gutschriftanzeigen werden zusammengeführt; technische IDs aus Änderungen werden entfernt. Pagination per limit, Sortierung Zeit absteigend; total vor Pagination. Diese Detailaktion führt keinen Zahlungsabgleich aus. Auf /list wird diese Aktion nur mit id als direktem Query oder filter-Eintrag ausgeführt; sonst folgt die gewöhnliche Mitgliedschaftsliste (einschließlich Zahlungsabgleich).","tags":["usermembership"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"property/value-Filter: id liest Details; user und plan filtern über öffentliche v2-IDs; status (pending_payment/scheduled/active/expired/exhausted/cancelled/payment_failed), active (FILTER_VALIDATE_BOOL), source (purchase/admin/public_purchase/admin_purchase/admin_grant/renewal/migration) und search (Vorname/Nachname/E-Mail). purchase umfasst beide Kaufkanäle, admin administrative Käufe und Vergaben. expression und unbekannte Eigenschaften werden ignoriert.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{},"expression":{"type":"string","description":"Ignoriert; Auswahl über property/value."}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"user","value":"user_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-Objekt start/reply oder Zahl als reply. Standard 0/100, reply auf 1–500 begrenzt, start mindestens 0.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","minimum":1,"maximum":500,"default":100}},"additionalProperties":true},{"type":"integer","minimum":1,"maximum":500}]},"example":{"start":0,"reply":25}}}},{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos.","examples":["usermembership_012345abcdef_123456"]},"description":"Einzelmitgliedschaft auswählen, alternativ filter-Eintrag id."},{"name":"user","in":"query","required":false,"schema":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Benutzers auswählen, alternativ filter-Eintrag user."},{"name":"plan","in":"query","required":false,"schema":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"description":"Mitgliedschaften eines Plans auswählen, alternativ filter-Eintrag plan."},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält das dokumentierte Ergebnis; error=false und success=true. Listen liefern zusätzlich total (Gesamtzahl vor Pagination).","content":{"application/json":{"example":{"success":true,"error":false,"data":[{"type":"redemption","event_type":"membership.usage_consumed","occurred_at":1790000000,"reason":"Buchung","actor":{"type":"system"},"resource":{"type":"membershipredemption","id":"membershipredemption_012345abcdef_123456"},"changes":{"quantity_delta":-1}}]},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["audit","redemption"]},"event_type":{"type":"string"},"occurred_at":{"type":"integer"},"reason":{"type":"string"},"actor":{"type":"object","properties":{"type":{"type":"string"}},"additionalProperties":true,"required":["type"]},"resource":{"type":"object","properties":{"type":{"type":"string"},"id":{"type":"string","description":"Öffentliche v2-ID des im type genannten Objekts."}},"additionalProperties":true,"required":["type","id"]},"changes":{"type":"object","properties":{"before":{"type":["object","null"],"additionalProperties":true,"description":"Zugelassene Zustandsfelder und explizit in v2 umgewandelte Ressourcen; rohe technische IDs werden entfernt."},"after":{"type":["object","null"],"additionalProperties":true,"description":"Zugelassene Zustandsfelder und explizit in v2 umgewandelte Ressourcen; rohe technische IDs werden entfernt."},"quantity_delta":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true,"required":["type","event_type","occurred_at","reason","actor","resource","changes"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^usermembership_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"version":{"type":"integer"},"user":{"type":"string","pattern":"^user_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"user_information":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true,"required":["name","email"]},"plan":{"type":"string","pattern":"^membershipplan_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"plan_version":{"type":"integer"},"name":{"type":"string"},"status":{"type":"string","enum":["pending_payment","scheduled","active","expired","exhausted","cancelled","payment_failed"]},"active":{"type":"boolean"},"valid_from":{"type":["integer","null"]},"valid_until":{"type":["integer","null"]},"granted_uses":{"type":["integer","null"]},"remaining_uses":{"type":["integer","null"]},"membership_promo_code":{"type":["string","null"]},"source":{"type":"string"},"renewal":{"type":"object","properties":{"auto_renew":{"type":"boolean"},"cancel_at_period_end":{"type":"boolean"},"cancel_requested_at":{"type":["integer","null"]},"cancel_effective_at":{"type":["integer","null"]},"next_renewal_at":{"type":["integer","null"]}},"additionalProperties":true},"current_period":{"type":["string","null"],"pattern":"^membershipperiod_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID des aktuellen Kontos."},"created_at":{"type":"integer"},"updated_at":{"type":"integer"}},"additionalProperties":true,"required":["id","version","user","user_information","plan","plan_version","name","status","active","valid_from","valid_until","granted_uses","remaining_uses","membership_promo_code","source","renewal","current_period","created_at","updated_at"]}}]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"x-edoobox-runtimeMutationGuard":true,"x-edoobox-get-effect":"business-mutation","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/01-membership.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Usermembership.php","method":"getUsermembership","line":24,"branch":{"file":"application/models/v2/Usermembership.php","line":34}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"usermembership","short":"usme","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/vat Mehrwertsteuersätze anzeigen Liest die im Konto angelegten Mehrwertsteuersätze einschließlich Land, Satz, B2B-Flag, Aktivierung und Standardkennzeichen. Unterstützt allgemeine Feldselektion, Filter, Sortierung und Pagination; die ID-Selektion bleibt standardmäßig eine ID-indizierte Sammlung. - Operation-ID: get_v2_vat - Dokumentation: /docs/api/v2/operations/get-v2-vat-b9e5e3957b76/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/vat' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/vat"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/vat" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_vat","summary":"Mehrwertsteuersätze anzeigen","description":"Liest die im Konto angelegten Mehrwertsteuersätze einschließlich Land, Satz, B2B-Flag, Aktivierung und Standardkennzeichen. Unterstützt allgemeine Feldselektion, Filter, Sortierung und Pagination; die ID-Selektion bleibt standardmäßig eine ID-indizierte Sammlung.","tags":["vat"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Mehrwertsteuersätzen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"vat_012345abcdef_123456":{"id":"vat_012345abcdef_123456","status":true,"description":"Normalsatz","vat_number":"EXAMPLE-VAT","vat_percent":8.1,"b2b":false,"country":"country_012345abcdef_123456","default":false,"last_change":"2026-10-15T10:00:00+02:00"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]},"status":{"type":"boolean"},"description":{"type":"string"},"vat_number":{"type":"string"},"vat_percent":{"type":"number"},"b2b":{"type":"boolean"},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["country_012345abcdef_123456"]},"default":{"type":"boolean"},"last_change":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]},"status":{"type":"boolean"},"description":{"type":"string"},"vat_number":{"type":"string"},"vat_percent":{"type":"number"},"b2b":{"type":"boolean"},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["country_012345abcdef_123456"]},"default":{"type":"boolean"},"last_change":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste. Leere Resultate können als [] ausgegeben werden."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Vat.php","method":"getVat","line":210},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"vat","short":"vat","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/vat Mehrwertsteuersatz anlegen Legt einen oder bis zu 100 Mehrwertsteuersätze an. description, vat_number, vat_percent und country sind erforderlich. country ist eine öffentliche country-ID. status ausdrücklich setzen: Beim Einzel-POST wird ein fehlender Status als inaktiv normalisiert. default=true entfernt den Standard anderer Sätze des Kontos. Zahlen werden gerundet und Flags normalisiert; die Feldschemas dokumentieren diese Normalisierung. - Operation-ID: post_v2_vat - Dokumentation: /docs/api/v2/operations/post-v2-vat-8efeed7736e8/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": "Normalsatz", "vat_number": "EXAMPLE-VAT", "vat_percent": 8.1, "country": "country_012345abcdef_123456", "status": true, "default": false } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/vat' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/vat"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": "Normalsatz", "vat_number": "EXAMPLE-VAT", "vat_percent": 8.1, "country": "country_012345abcdef_123456", "status": true, "default": false } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/vat" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":\"Normalsatz\",\"vat_number\":\"EXAMPLE-VAT\",\"vat_percent\":8.1,\"country\":\"country_012345abcdef_123456\",\"status\":true,\"default\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_vat","summary":"Mehrwertsteuersatz anlegen","description":"Legt einen oder bis zu 100 Mehrwertsteuersätze an. description, vat_number, vat_percent und country sind erforderlich. country ist eine öffentliche country-ID. status ausdrücklich setzen: Beim Einzel-POST wird ein fehlender Status als inaktiv normalisiert. default=true entfernt den Standard anderer Sätze des Kontos. Zahlen werden gerundet und Flags normalisiert; die Feldschemas dokumentieren diese Normalisierung.","tags":["vat"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"insert=true und öffentliche ID; bei Batch eine ID-Liste.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"insert":true,"id":"vat_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"anyOf":[{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]}}]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"status":{"description":"Canonical boolean; POST getter normalizes loose truthiness. Absent status injected as numeric 0 on single POST and becomes inactive; batch missing status has no injection.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":38,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Vatfunc.php","line":31,"reason":"Actual Vatfunc hook; Permissionfunc same-named method is not dispatched.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":40,"reason":"POST before-insert default behavior; no update validation call.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":80,"reason":"Raw-truthiness validation forbids removal/deactivation of current default and status=false with default=true.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"},{"file":"application/models/v2/Vatfunc.php","line":58,"lineEnd":70,"reason":"Every update resolves the VAT row by id and firma_info_id; missing or foreign rows throw ED80100 before default-clearing writes.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":72,"lineEnd":78,"reason":"Both field hooks merge the same aktiv value: nonzero integer default activates; otherwise supplied integer status wins; otherwise existing activity remains. JSON key order cannot restore the old activity.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"}]},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"vat_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":62},"vat_percent":{"anyOf":[{"type":"number"},{"type":"string","description":"Numeric string accepted by PHP number_format."},{"type":"boolean"},{"type":"null"}],"description":"Required on create by Structure require.insert. number_format rounds/formats to 2 decimals before persistence; no application-level min/max/multipleOf rejection.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-canonicalType":"number","examples":[8.1,"8.10"],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":75,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1797,"reason":"Direct update getter, no resource hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":543,"reason":"Structure require.insert marks presence required.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1331,"reason":"Missing single-create required field rejects request.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"b2b":{"description":"Only literal JSON true enables B2B; all other values normalize to 0. No type rejection.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":88,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1797,"reason":"No hook; direct getter produces 1/0, never strict false.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":97},"default":{"description":"Canonical boolean; POST getter normalizes loose truthiness. Before-insert true default resets other account defaults; does not validate active status.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":110,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Vatfunc.php","line":10,"reason":"Actual Vatfunc hook; Permissionfunc same-named method is not dispatched.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":40,"reason":"POST before-insert default behavior; no update validation call.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":80,"reason":"Raw-truthiness validation forbids removal/deactivation of current default and status=false with default=true.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"},{"file":"application/models/v2/Vatfunc.php","line":58,"lineEnd":70,"reason":"Every update resolves the VAT row by id and firma_info_id; missing or foreign rows throw ED80100 before default-clearing writes.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":72,"lineEnd":78,"reason":"Both field hooks merge the same aktiv value: nonzero integer default activates; otherwise supplied integer status wins; otherwise existing activity remains. JSON key order cannot restore the old activity.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"}]},"last_change":{"description":"Client value ignored; getter returns server time(). Single POST injects absent last_change; batch only processes supplied field.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-ignoredInput":true,"examples":["2001-01-01T00:00:00Z",null],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":120,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1779,"reason":"PUT injects update_default when absent.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch POST only processes supplied values.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"reason":"Actual dQ date-time/false output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["description","vat_number","vat_percent","country"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"status":{"description":"Canonical boolean; POST getter normalizes loose truthiness. Absent status injected as numeric 0 on single POST and becomes inactive; batch missing status has no injection.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":38,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Vatfunc.php","line":31,"reason":"Actual Vatfunc hook; Permissionfunc same-named method is not dispatched.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":40,"reason":"POST before-insert default behavior; no update validation call.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":80,"reason":"Raw-truthiness validation forbids removal/deactivation of current default and status=false with default=true.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"},{"file":"application/models/v2/Vatfunc.php","line":58,"lineEnd":70,"reason":"Every update resolves the VAT row by id and firma_info_id; missing or foreign rows throw ED80100 before default-clearing writes.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":72,"lineEnd":78,"reason":"Both field hooks merge the same aktiv value: nonzero integer default activates; otherwise supplied integer status wins; otherwise existing activity remains. JSON key order cannot restore the old activity.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"}]},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"vat_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":62},"vat_percent":{"anyOf":[{"type":"number"},{"type":"string","description":"Numeric string accepted by PHP number_format."},{"type":"boolean"},{"type":"null"}],"description":"Required on create by Structure require.insert. number_format rounds/formats to 2 decimals before persistence; no application-level min/max/multipleOf rejection.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-canonicalType":"number","examples":[8.1,"8.10"],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":75,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1797,"reason":"Direct update getter, no resource hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":543,"reason":"Structure require.insert marks presence required.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1331,"reason":"Missing single-create required field rejects request.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"b2b":{"description":"Only literal JSON true enables B2B; all other values normalize to 0. No type rejection.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":88,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1797,"reason":"No hook; direct getter produces 1/0, never strict false.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":97},"default":{"description":"Canonical boolean; POST getter normalizes loose truthiness. Before-insert true default resets other account defaults; does not validate active status.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":110,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Vatfunc.php","line":10,"reason":"Actual Vatfunc hook; Permissionfunc same-named method is not dispatched.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":40,"reason":"POST before-insert default behavior; no update validation call.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":80,"reason":"Raw-truthiness validation forbids removal/deactivation of current default and status=false with default=true.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"},{"file":"application/models/v2/Vatfunc.php","line":58,"lineEnd":70,"reason":"Every update resolves the VAT row by id and firma_info_id; missing or foreign rows throw ED80100 before default-clearing writes.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":72,"lineEnd":78,"reason":"Both field hooks merge the same aktiv value: nonzero integer default activates; otherwise supplied integer status wins; otherwise existing activity remains. JSON key order cannot restore the old activity.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"}]},"last_change":{"description":"Client value ignored; getter returns server time(). Single POST injects absent last_change; batch only processes supplied field.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-ignoredInput":true,"examples":["2001-01-01T00:00:00Z",null],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":120,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1779,"reason":"PUT injects update_default when absent.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch POST only processes supplied values.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"reason":"Actual dQ date-time/false output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["description","vat_number","vat_percent","country"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["vat_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mehrwertsteuersatz anlegen","value":{"data":{"description":"Normalsatz","vat_number":"EXAMPLE-VAT","vat_percent":8.1,"country":"country_012345abcdef_123456","status":true,"default":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Vat.php","method":"postVat","line":152},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"vat","short":"vat","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/vat Mehrwertsteuersatz aktualisieren Aktualisiert die übergebenen Felder eines eigenen Mehrwertsteuersatzes und leert den Steuer-Cache. Der aktuelle Standardsatz darf nicht deaktiviert und sein Standardkennzeichen nicht entfernt werden. Ein neuer Standard aktiviert den Satz und entfernt andere Standards. Die ID wird im Pfad oder beim Basisendpunkt im äußeren id-Feld übergeben. - Operation-ID: put_v2_vat - Dokumentation: /docs/api/v2/operations/put-v2-vat-1e5d6c8a5849/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": "Aktualisierter Normalsatz", "status": true }, "id": "vat_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/vat' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/vat"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": "Aktualisierter Normalsatz", "status": true }, "id": "vat_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/vat" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":\"Aktualisierter Normalsatz\",\"status\":true},\"id\":\"vat_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_vat","summary":"Mehrwertsteuersatz aktualisieren","description":"Aktualisiert die übergebenen Felder eines eigenen Mehrwertsteuersatzes und leert den Steuer-Cache. Der aktuelle Standardsatz darf nicht deaktiviert und sein Standardkennzeichen nicht entfernt werden. Ein neuer Standard aktiviert den Satz und entfernt andere Standards. Die ID wird im Pfad oder beim Basisendpunkt im äußeren id-Feld übergeben.","tags":["vat"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update und öffentliche ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"vat_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"description":"Canonical boolean. Native PUT derives activity from integer casts, not a rejecting boolean/0-or-1 input guard. Both field hooks use the same activity rule: nonzero default activates; otherwise supplied status selects active/inactive; otherwise current activity remains. The VAT must exist in this account before any default clear. Existing raw-truthiness validation still rejects unsetting/deactivating a current default or raw status=false with raw default=true. Thus status:false plus default:false deactivates a nondefault VAT in either JSON key order. POST uses its loose-truthiness getter and separate before-insert hook.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":38,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Vatfunc.php","line":31,"reason":"Actual Vatfunc hook; Permissionfunc same-named method is not dispatched.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":40,"reason":"POST before-insert default behavior; no update validation call.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":80,"reason":"Raw-truthiness validation forbids removal/deactivation of current default and status=false with default=true.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"},{"file":"application/models/v2/Vatfunc.php","line":58,"lineEnd":70,"reason":"Every update resolves the VAT row by id and firma_info_id; missing or foreign rows throw ED80100 before default-clearing writes.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":72,"lineEnd":78,"reason":"Both field hooks merge the same aktiv value: nonzero integer default activates; otherwise supplied integer status wins; otherwise existing activity remains. JSON key order cannot restore the old activity.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"}]},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"vat_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":62},"vat_percent":{"anyOf":[{"type":"number"},{"type":"string","description":"Numeric string accepted by PHP number_format."},{"type":"boolean"},{"type":"null"}],"description":"number_format rounds/formats to 2 decimals before persistence. Numeric strings supported; no min/max/multipleOf rejection.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-canonicalType":"number","examples":[8.1],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":75,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1797,"reason":"Direct update getter, no resource hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":543,"reason":"Structure require.insert marks presence required.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1331,"reason":"Missing single-create required field rejects request.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"b2b":{"description":"Only literal JSON true enables B2B; all other values normalize to 0. No type rejection.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":88,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1797,"reason":"No hook; direct getter produces 1/0, never strict false.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":97},"default":{"description":"Canonical boolean. Native PUT casts raw default to int, stores that integer and resets other account defaults when nonzero. Both field hooks use the same activity rule: nonzero default activates; otherwise supplied status selects active/inactive; otherwise current activity remains. The VAT must exist in this account before any default clear. Existing raw-truthiness validation still rejects unsetting/deactivating a current default or raw status=false with raw default=true. Thus status:false plus default:false deactivates a nondefault VAT in either JSON key order. POST uses its loose-truthiness getter and separate before-insert hook.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":110,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Vatfunc.php","line":10,"reason":"Actual Vatfunc hook; Permissionfunc same-named method is not dispatched.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":40,"reason":"POST before-insert default behavior; no update validation call.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":80,"reason":"Raw-truthiness validation forbids removal/deactivation of current default and status=false with default=true.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"},{"file":"application/models/v2/Vatfunc.php","line":58,"lineEnd":70,"reason":"Every update resolves the VAT row by id and firma_info_id; missing or foreign rows throw ED80100 before default-clearing writes.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":72,"lineEnd":78,"reason":"Both field hooks merge the same aktiv value: nonzero integer default activates; otherwise supplied integer status wins; otherwise existing activity remains. JSON key order cannot restore the old activity.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"}]},"last_change":{"description":"Client value ignored; server time used, including update_default injection when absent.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-ignoredInput":true,"examples":["2001-01-01T00:00:00Z",null],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":120,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1779,"reason":"PUT injects update_default when absent.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch POST only processes supplied values.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"reason":"Actual dQ date-time/false output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"4a3097e873943fa5090a2fd52086762dc36a50d399bdd78ad6b20e6ed595639a"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["vat_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Mehrwertsteuersatz aktualisieren","value":{"data":{"description":"Aktualisierter Normalsatz","status":true},"id":"vat_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Vat.php","method":"updateVat","line":171},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"vat","short":"vat","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/vat/{id} Mehrwertsteuersatz prüfen Liest die im Konto angelegten Mehrwertsteuersätze einschließlich Land, Satz, B2B-Flag, Aktivierung und Standardkennzeichen. Unterstützt allgemeine Feldselektion, Filter, Sortierung und Pagination; die ID-Selektion bleibt standardmäßig eine ID-indizierte Sammlung. - Operation-ID: get_v2_vat_id - Dokumentation: /docs/api/v2/operations/get-v2-vat-id-33c434de3d80/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["vat_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/vat/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/vat/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/vat/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_vat_id","summary":"Mehrwertsteuersatz prüfen","description":"Liest die im Konto angelegten Mehrwertsteuersätze einschließlich Land, Satz, B2B-Flag, Aktivierung und Standardkennzeichen. Unterstützt allgemeine Feldselektion, Filter, Sortierung und Pagination; die ID-Selektion bleibt standardmäßig eine ID-indizierte Sammlung.","tags":["vat"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["vat_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Mehrwertsteuersätzen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"vat_012345abcdef_123456":{"id":"vat_012345abcdef_123456","status":true,"description":"Normalsatz","vat_number":"EXAMPLE-VAT","vat_percent":8.1,"b2b":false,"country":"country_012345abcdef_123456","default":false,"last_change":"2026-10-15T10:00:00+02:00"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]},"status":{"type":"boolean"},"description":{"type":"string"},"vat_number":{"type":"string"},"vat_percent":{"type":"number"},"b2b":{"type":"boolean"},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["country_012345abcdef_123456"]},"default":{"type":"boolean"},"last_change":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]},"status":{"type":"boolean"},"description":{"type":"string"},"vat_number":{"type":"string"},"vat_percent":{"type":"number"},"b2b":{"type":"boolean"},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["country_012345abcdef_123456"]},"default":{"type":"boolean"},"last_change":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste. Leere Resultate können als [] ausgegeben werden."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Vat.php","method":"getVat","line":210},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"vat","short":"vat","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/vat/{id} Mehrwertsteuersatz aktualisieren Aktualisiert die übergebenen Felder eines eigenen Mehrwertsteuersatzes und leert den Steuer-Cache. Der aktuelle Standardsatz darf nicht deaktiviert und sein Standardkennzeichen nicht entfernt werden. Ein neuer Standard aktiviert den Satz und entfernt andere Standards. Die ID wird im Pfad oder beim Basisendpunkt im äußeren id-Feld übergeben. - Operation-ID: put_v2_vat_id - Dokumentation: /docs/api/v2/operations/put-v2-vat-id-e807c57f274a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["vat_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "description": "Aktualisierter Normalsatz", "status": true } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/vat/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/vat/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "description": "Aktualisierter Normalsatz", "status": true } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/vat/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"description\":\"Aktualisierter Normalsatz\",\"status\":true}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_vat_id","summary":"Mehrwertsteuersatz aktualisieren","description":"Aktualisiert die übergebenen Felder eines eigenen Mehrwertsteuersatzes und leert den Steuer-Cache. Der aktuelle Standardsatz darf nicht deaktiviert und sein Standardkennzeichen nicht entfernt werden. Ein neuer Standard aktiviert den Satz und entfernt andere Standards. Die ID wird im Pfad oder beim Basisendpunkt im äußeren id-Feld übergeben.","tags":["vat"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["vat_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"update und öffentliche ID.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"update":true,"id":"vat_012345abcdef_123456"}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"update":{"type":"boolean"},"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]}},"additionalProperties":true},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"status":{"description":"Canonical boolean. Native PUT derives activity from integer casts, not a rejecting boolean/0-or-1 input guard. Both field hooks use the same activity rule: nonzero default activates; otherwise supplied status selects active/inactive; otherwise current activity remains. The VAT must exist in this account before any default clear. Existing raw-truthiness validation still rejects unsetting/deactivating a current default or raw status=false with raw default=true. Thus status:false plus default:false deactivates a nondefault VAT in either JSON key order. POST uses its loose-truthiness getter and separate before-insert hook.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":38,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Vatfunc.php","line":31,"reason":"Actual Vatfunc hook; Permissionfunc same-named method is not dispatched.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":40,"reason":"POST before-insert default behavior; no update validation call.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":80,"reason":"Raw-truthiness validation forbids removal/deactivation of current default and status=false with default=true.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"},{"file":"application/models/v2/Vatfunc.php","line":58,"lineEnd":70,"reason":"Every update resolves the VAT row by id and firma_info_id; missing or foreign rows throw ED80100 before default-clearing writes.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":72,"lineEnd":78,"reason":"Both field hooks merge the same aktiv value: nonzero integer default activates; otherwise supplied integer status wins; otherwise existing activity remains. JSON key order cannot restore the old activity.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"}]},"description":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":49},"vat_number":{"x-edoobox-canonicalType":"string","x-edoobox-maxStoredLength":250,"x-edoobox-normalization":"The aP getter trims text, replaces newlines and limits the legacy escaped storage representation to maxStoredLength UTF-8 characters without splitting escape pairs; aQ decodes the stored representation. Non-string input becomes empty text. This getter limit is normalization, not a rejecting input maxLength or proof of a custom action hook or database column capacity.","examples":["Example"],"x-edoobox-requiredOnCreate":true,"x-edoobox-sourceLine":62},"vat_percent":{"anyOf":[{"type":"number"},{"type":"string","description":"Numeric string accepted by PHP number_format."},{"type":"boolean"},{"type":"null"}],"description":"number_format rounds/formats to 2 decimals before persistence. Numeric strings supported; no min/max/multipleOf rejection.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-canonicalType":"number","examples":[8.1],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":75,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1797,"reason":"Direct update getter, no resource hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":543,"reason":"Structure require.insert marks presence required.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1331,"reason":"Missing single-create required field rejects request.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"b2b":{"description":"Only literal JSON true enables B2B; all other values normalize to 0. No type rejection.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[true,false],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":88,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1797,"reason":"No hook; direct getter produces 1/0, never strict false.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]},"country":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["country_012345abcdef_123456"],"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":97},"default":{"description":"Canonical boolean. Native PUT casts raw default to int, stores that integer and resets other account defaults when nonzero. Both field hooks use the same activity rule: nonzero default activates; otherwise supplied status selects active/inactive; otherwise current activity remains. The VAT must exist in this account before any default clear. Existing raw-truthiness validation still rejects unsetting/deactivating a current default or raw status=false with raw default=true. Thus status:false plus default:false deactivates a nondefault VAT in either JSON key order. POST uses its loose-truthiness getter and separate before-insert hook.","x-edoobox-schemaStatus":"source-reviewed-normalization","x-edoobox-canonicalType":"boolean","examples":[false,true],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":110,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1792,"reason":"PUT tests update flag then dispatches exact resource hook: true succeeds, array merges, false returns ED90100.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/Vatfunc.php","line":10,"reason":"Actual Vatfunc hook; Permissionfunc same-named method is not dispatched.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":40,"reason":"POST before-insert default behavior; no update validation call.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":80,"reason":"Raw-truthiness validation forbids removal/deactivation of current default and status=false with default=true.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"},{"file":"application/models/v2/Vatfunc.php","line":58,"lineEnd":70,"reason":"Every update resolves the VAT row by id and firma_info_id; missing or foreign rows throw ED80100 before default-clearing writes.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"},{"file":"application/models/v2/Vatfunc.php","line":72,"lineEnd":78,"reason":"Both field hooks merge the same aktiv value: nonzero integer default activates; otherwise supplied integer status wins; otherwise existing activity remains. JSON key order cannot restore the old activity.","sha256":"3d8cef71f39ce3b4cc04f2adc95b8ee0f8d490983db2d55e1f6ec6445a2c63ee"}]},"last_change":{"description":"Client value ignored; server time used, including update_default injection when absent.","x-edoobox-schemaStatus":"source-reviewed","x-edoobox-ignoredInput":true,"examples":["2001-01-01T00:00:00Z",null],"x-edoobox-contractSource":[{"file":"application/models/v2/Vat.php","line":120,"reason":"Exact Structure declaration: get converts incoming wire input; set converts response output.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/Vat.php","line":152,"reason":"Exact POST/PUT callers use API_DATA and Modal Vat.","sha256":"e1cdb6c7a92dd3e1fe2fb08e96946d3a70502cbc1d12fa7b572eae1d170f0eef"},{"file":"application/models/v2/API_DATA.php","line":1278,"reason":"Single POST injects absent insert_default as 0, executes incoming getters (not update func) and calls before-insert hook.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1779,"reason":"PUT injects update_default when absent.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/models/v2/API_DATA.php","line":1568,"reason":"Batch POST only processes supplied values.","sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c"},{"file":"application/helpers/edooboxhelp_helper.php","line":11629,"reason":"Actual dQ date-time/false output.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"docs/openapi/reviews/payment-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"15fa965103eef0f7cdfc6a2263b4a2358b739c3b4f9d6a377dc7033ac872e0ce"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["vat_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Mehrwertsteuersatz aktualisieren","value":{"data":{"description":"Aktualisierter Normalsatz","status":true}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Vat.php","method":"updateVat","line":171},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"vat","short":"vat","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/vat/list Mehrwertsteuersätze anzeigen Liest die im Konto angelegten Mehrwertsteuersätze einschließlich Land, Satz, B2B-Flag, Aktivierung und Standardkennzeichen. Unterstützt allgemeine Feldselektion, Filter, Sortierung und Pagination; die ID-Selektion bleibt standardmäßig eine ID-indizierte Sammlung. - Operation-ID: get_v2_vat_list - Dokumentation: /docs/api/v2/operations/get-v2-vat-list-90d4aacd254c/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/vat/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/vat/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/vat/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_vat_list","summary":"Mehrwertsteuersätze anzeigen","description":"Liest die im Konto angelegten Mehrwertsteuersätze einschließlich Land, Satz, B2B-Flag, Aktivierung und Standardkennzeichen. Unterstützt allgemeine Feldselektion, Filter, Sortierung und Pagination; die ID-Selektion bleibt standardmäßig eine ID-indizierte Sammlung.","tags":["vat"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"vat_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"ID-indizierte Sammlung oder Liste von Mehrwertsteuersätzen mit limit-Metadaten.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"vat_012345abcdef_123456":{"id":"vat_012345abcdef_123456","status":true,"description":"Normalsatz","vat_number":"EXAMPLE-VAT","vat_percent":8.1,"b2b":false,"country":"country_012345abcdef_123456","default":false,"last_change":"2026-10-15T10:00:00+02:00"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]},"status":{"type":"boolean"},"description":{"type":"string"},"vat_number":{"type":"string"},"vat_percent":{"type":"number"},"b2b":{"type":"boolean"},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["country_012345abcdef_123456"]},"default":{"type":"boolean"},"last_change":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^vat_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["vat_012345abcdef_123456"]},"status":{"type":"boolean"},"description":{"type":"string"},"vat_number":{"type":"string"},"vat_percent":{"type":"number"},"b2b":{"type":"boolean"},"country":{"type":"string","pattern":"^country_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine interne Datenbank-ID.","examples":["country_012345abcdef_123456"]},"default":{"type":"boolean"},"last_change":{"anyOf":[{"type":"string"},{"const":false}]}},"additionalProperties":true}}],"description":"Standard: nach öffentlicher ID indiziertes Objekt; response_type=array liefert eine Liste. Leere Resultate können als [] ausgegeben werden."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/04-accounting.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Vat.php","method":"getVat","line":210},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"vat","short":"vat","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/webhook Webhook-abonnement entfernen Löscht die Webhook-Konfiguration des ausgewählten Kontos dauerhaft. Nachfolgende Ereignisse werden nicht mehr über dieses Abonnement versendet. Die öffentliche id ist als Query-Parameter oder im JSON-Umschlag erforderlich. - Operation-ID: delete_v2_webhook - Dokumentation: /docs/api/v2/operations/delete-v2-webhook-9aa4752d806a/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | nein | Alternativ zu id im JSON-Umschlag; einer dieser Selektoren ist erforderlich. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["webhook_012345abcdef_123456"]},"description":"Alternativ zu id im JSON-Umschlag; einer dieser Selektoren ist erforderlich."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "webhook_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/webhook' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/webhook"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "webhook_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/webhook" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"webhook_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_webhook","summary":"Webhook-abonnement entfernen","description":"Löscht die Webhook-Konfiguration des ausgewählten Kontos dauerhaft. Nachfolgende Ereignisse werden nicht mehr über dieses Abonnement versendet. Die öffentliche id ist als Query-Parameter oder im JSON-Umschlag erforderlich.","tags":["webhook"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID.","examples":["webhook_012345abcdef_123456"]},"description":"Alternativ zu id im JSON-Umschlag; einer dieser Selektoren ist erforderlich."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche ID und Löschstatus; delete=false bedeutet, dass kein Datensatz entfernt wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"webhook_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Webhook-abonnement entfernen","value":{"id":"webhook_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Webhook.php","method":"deleteWebhook","line":196},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"webhook","short":"wbho","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/webhook Webhook-Abonnements auflisten Liest konfigurierte Webhook-Zieladressen, Aktivierung und abonnierten Themen. secret und basicauth werden durch die bestehende Maskierung geschützt; maskierte Werte sind keine wiederverwendbaren Zugangsdaten. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_webhook - Dokumentation: /docs/api/v2/operations/get-v2-webhook-55b2421efa1e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/webhook' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/webhook"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/webhook" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_webhook","summary":"Webhook-Abonnements auflisten","description":"Liest konfigurierte Webhook-Zieladressen, Aktivierung und abonnierten Themen. secret und basicauth werden durch die bestehende Maskierung geschützt; maskierte Werte sind keine wiederverwendbaren Zugangsdaten. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["webhook"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"webhook_012345abcdef_123456":{"id":"webhook_012345abcdef_123456","status":true,"url":"https://example.org/edoobox-hook","secret":false,"basicauth":false,"topics":["post_Booking"]}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitiveResponseFields":["secret"],"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Webhook.php","method":"getWebhook","line":212},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"webhook","short":"wbho","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/webhook Webhook-abonnement anlegen Registriert ein Webhook-Abonnement für spätere Ressourcenänderungen. url und topics sind Pflicht; fehlendes secret/basicauth wird auf leer gesetzt, status standardmäßig aktiviert. Themen verwenden post_, put_ oder delete_ mit Ressourcenname (z.B. post_Booking). Unbekannte oder doppelte Themen werden ignoriert. Die Speicherung verschickt hier noch keinen Test-Webhook. data erlaubt ein einzelnes Objekt oder höchstens 100 Objekte als Batch; die Antwort enthält eine ID bzw. eine ID-Liste. - Operation-ID: post_v2_webhook - Dokumentation: /docs/api/v2/operations/post-v2-webhook-9ad5563f711d/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "url": "https://example.org/edoobox-hook", "status": true, "secret": false, "topics": [ "post_Booking", "put_User" ] } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/webhook' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/webhook"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "url": "https://example.org/edoobox-hook", "status": true, "secret": false, "topics": [ "post_Booking", "put_User" ] } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/webhook" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"url\":\"https://example.org/edoobox-hook\",\"status\":true,\"secret\":false,\"topics\":[\"post_Booking\",\"put_User\"]}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_webhook","summary":"Webhook-abonnement anlegen","description":"Registriert ein Webhook-Abonnement für spätere Ressourcenänderungen. url und topics sind Pflicht; fehlendes secret/basicauth wird auf leer gesetzt, status standardmäßig aktiviert. Themen verwenden post_, put_ oder delete_ mit Ressourcenname (z.B. post_Booking). Unbekannte oder doppelte Themen werden ignoriert. Die Speicherung verschickt hier noch keinen Test-Webhook. data erlaubt ein einzelnes Objekt oder höchstens 100 Objekte als Batch; die Antwort enthält eine ID bzw. eine ID-Liste.","tags":["webhook"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id enthält die neue öffentliche ID oder beim Batch eine Liste; insert zeigt das Ergebnis.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"webhook_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"anyOf":[{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},{"type":"array","items":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},{"type":"null"}]},"insert":{"type":"boolean"}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"url":{"type":"string","format":"uri","description":"Muss FILTER_VALIDATE_URL bestehen."},"secret":{"type":["string","number","boolean","null","array","object"],"description":"HMAC-Secret als String; false oder leer deaktiviert die Signatur. Ausgabe ist maskiert.","examples":[false]},"basicauth":{"type":["string","number","boolean","null","array","object"],"description":"Optionaler Basic-Auth-Wert username:password als String; false deaktiviert ihn. Ausgabe ist maskiert.","examples":[false]},"topics":{"type":"array","items":{"type":"string"},"description":"Themen werden normalisiert zu post_Resource, put_Resource oder delete_Resource. Nur vorhandene Ressourcenmodelle werden übernommen; unbekannte Einträge werden ignoriert."}},"additionalProperties":true,"required":["url","topics"]},{"type":"array","items":{"type":"object","properties":{"status":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"url":{"type":"string","format":"uri","description":"Muss FILTER_VALIDATE_URL bestehen."},"secret":{"type":["string","number","boolean","null","array","object"],"description":"HMAC-Secret als String; false oder leer deaktiviert die Signatur. Ausgabe ist maskiert.","examples":[false]},"basicauth":{"type":["string","number","boolean","null","array","object"],"description":"Optionaler Basic-Auth-Wert username:password als String; false deaktiviert ihn. Ausgabe ist maskiert.","examples":[false]},"topics":{"type":"array","items":{"type":"string"},"description":"Themen werden normalisiert zu post_Resource, put_Resource oder delete_Resource. Nur vorhandene Ressourcenmodelle werden übernommen; unbekannte Einträge werden ignoriert."}},"additionalProperties":true,"required":["url","topics"]},"minItems":1,"maxItems":100}]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Webhook-abonnement anlegen","value":{"data":{"url":"https://example.org/edoobox-hook","status":true,"secret":false,"topics":["post_Booking","put_User"]}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Webhook.php","method":"postWebhook","line":162},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"webhook","short":"wbho","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/webhook Webhook-abonnement ändern Ändert Zieladresse, Aktivierung, HMAC-Secret, Basic Auth oder Themen eines bestehenden Abonnements. topics ersetzt die Themenliste; nicht mehr enthaltene Themen werden entfernt. Neue Themen werden auf gültige Ressourcen und Methoden geprüft. id im JSON-Umschlag ist Pflicht. - Operation-ID: put_v2_webhook - Dokumentation: /docs/api/v2/operations/put-v2-webhook-95bf97eeda14/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false }, "id": "webhook_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/webhook' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/webhook"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false }, "id": "webhook_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/webhook" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false},\"id\":\"webhook_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_webhook","summary":"Webhook-abonnement ändern","description":"Ändert Zieladresse, Aktivierung, HMAC-Secret, Basic Auth oder Themen eines bestehenden Abonnements. topics ersetzt die Themenliste; nicht mehr enthaltene Themen werden entfernt. Neue Themen werden auf gültige Ressourcen und Methoden geprüft. id im JSON-Umschlag ist Pflicht.","tags":["webhook"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, update das tatsächliche Änderungsergebnis; update=false ist keine Bestätigung einer Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"webhook_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"url":{"type":"string","format":"uri","description":"Muss FILTER_VALIDATE_URL bestehen."},"secret":{"type":["string","number","boolean","null","array","object"],"description":"HMAC-Secret als String; false oder leer deaktiviert die Signatur. Ausgabe ist maskiert.","examples":[false]},"basicauth":{"type":["string","number","boolean","null","array","object"],"description":"Optionaler Basic-Auth-Wert username:password als String; false deaktiviert ihn. Ausgabe ist maskiert.","examples":[false]},"topics":{"type":"array","items":{"type":"string"},"description":"Themen werden normalisiert zu post_Resource, put_Resource oder delete_Resource. Nur vorhandene Ressourcenmodelle werden übernommen; unbekannte Einträge werden ignoriert."}},"additionalProperties":true},"id":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Webhook-abonnement ändern","value":{"data":{"status":false},"id":"webhook_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Webhook.php","method":"updateWebhook","line":179},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"webhook","short":"wbho","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/webhook/{id} Webhook-abonnement entfernen Löscht die Webhook-Konfiguration des ausgewählten Kontos dauerhaft. Nachfolgende Ereignisse werden nicht mehr über dieses Abonnement versendet. Die öffentliche ID im Pfad wählt das Objekt. - Operation-ID: delete_v2_webhook_id - Dokumentation: /docs/api/v2/operations/delete-v2-webhook-id-4e5c1b5c2519/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "webhook_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/webhook/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/webhook/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "webhook_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/webhook/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"webhook_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_webhook_id","summary":"Webhook-abonnement entfernen","description":"Löscht die Webhook-Konfiguration des ausgewählten Kontos dauerhaft. Nachfolgende Ereignisse werden nicht mehr über dieses Abonnement versendet. Die öffentliche ID im Pfad wählt das Objekt.","tags":["webhook"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"Öffentliche ID und Löschstatus; delete=false bedeutet, dass kein Datensatz entfernt wurde.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"webhook_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."}},"additionalProperties":true},"examples":{"request":{"summary":"Webhook-abonnement entfernen","value":{"id":"webhook_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Webhook.php","method":"deleteWebhook","line":196},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"webhook","short":"wbho","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/webhook/{id} Webhook-abonnement lesen Liest konfigurierte Webhook-Zieladressen, Aktivierung und abonnierten Themen. secret und basicauth werden durch die bestehende Maskierung geschützt; maskierte Werte sind keine wiederverwendbaren Zugangsdaten. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_webhook_id - Dokumentation: /docs/api/v2/operations/get-v2-webhook-id-e2a474c807cd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/webhook/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/webhook/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/webhook/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_webhook_id","summary":"Webhook-abonnement lesen","description":"Liest konfigurierte Webhook-Zieladressen, Aktivierung und abonnierten Themen. secret und basicauth werden durch die bestehende Maskierung geschützt; maskierte Werte sind keine wiederverwendbaren Zugangsdaten. Die öffentliche ID im Pfad begrenzt die Ergebnismenge auf dieses Objekt. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["webhook"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"webhook_012345abcdef_123456":{"id":"webhook_012345abcdef_123456","status":true,"url":"https://example.org/edoobox-hook","secret":false,"basicauth":false,"topics":["post_Booking"]}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitiveResponseFields":["secret"],"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Webhook.php","method":"getWebhook","line":212},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"webhook","short":"wbho","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/webhook/{id} Webhook-abonnement ändern Ändert Zieladresse, Aktivierung, HMAC-Secret, Basic Auth oder Themen eines bestehenden Abonnements. topics ersetzt die Themenliste; nicht mehr enthaltene Themen werden entfernt. Neue Themen werden auf gültige Ressourcen und Methoden geprüft. Die öffentliche ID im Pfad wählt das Objekt; ein zusätzliches id im Umschlag hat beim Legacy-Normalisierer Vorrang. - Operation-ID: put_v2_webhook_id - Dokumentation: /docs/api/v2/operations/put-v2-webhook-id-8276de4ad3cd/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "status": false } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/webhook/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/webhook/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "status": false } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/webhook/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"status\":false}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_webhook_id","summary":"Webhook-abonnement ändern","description":"Ändert Zieladresse, Aktivierung, HMAC-Secret, Basic Auth oder Themen eines bestehenden Abonnements. topics ersetzt die Themenliste; nicht mehr enthaltene Themen werden entfernt. Neue Themen werden auf gültige Ressourcen und Methoden geprüft. Die öffentliche ID im Pfad wählt das Objekt; ein zusätzliches id im Umschlag hat beim Legacy-Normalisierer Vorrang.","tags":["webhook"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, update das tatsächliche Änderungsergebnis; update=false ist keine Bestätigung einer Änderung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"webhook_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^webhook_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus dem ausgewählten Konto; keine interne Datenbank-ID."},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":["string","number","boolean","null","array","object"],"description":"Wird nach PHP-Wahrheitswert zu true/false normalisiert; boolesche JSON-Werte verwenden.","examples":[true]},"url":{"type":"string","format":"uri","description":"Muss FILTER_VALIDATE_URL bestehen."},"secret":{"type":["string","number","boolean","null","array","object"],"description":"HMAC-Secret als String; false oder leer deaktiviert die Signatur. Ausgabe ist maskiert.","examples":[false]},"basicauth":{"type":["string","number","boolean","null","array","object"],"description":"Optionaler Basic-Auth-Wert username:password als String; false deaktiviert ihn. Ausgabe ist maskiert.","examples":[false]},"topics":{"type":"array","items":{"type":"string"},"description":"Themen werden normalisiert zu post_Resource, put_Resource oder delete_Resource. Nur vorhandene Ressourcenmodelle werden übernommen; unbekannte Einträge werden ignoriert."}},"additionalProperties":true}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Webhook-abonnement ändern","value":{"data":{"status":false}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Webhook.php","method":"updateWebhook","line":179},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"webhook","short":"wbho","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/webhook/list Webhook-Abonnements auflisten Liest konfigurierte Webhook-Zieladressen, Aktivierung und abonnierten Themen. secret und basicauth werden durch die bestehende Maskierung geschützt; maskierte Werte sind keine wiederverwendbaren Zugangsdaten. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste. - Operation-ID: get_v2_webhook_list - Dokumentation: /docs/api/v2/operations/get-v2-webhook-list-8d6b9e105598/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | nein | JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/webhook/list' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/webhook/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/webhook/list" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_webhook_list","summary":"Webhook-Abonnements auflisten","description":"Liest konfigurierte Webhook-Zieladressen, Aktivierung und abonnierten Themen. secret und basicauth werden durch die bestehende Maskierung geschützt; maskierte Werte sind keine wiederverwendbaren Zugangsdaten. fields, filter, limit und order wählen Felder und Ergebnismenge. data bleibt auch bei einer einzelnen ID eine ID-indizierte Sammlung; response_type=array liefert eine Liste.","tags":["webhook"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":false,"description":"JSON-encoded filter value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"expression":{"type":"string"},"value":{}},"additionalProperties":true},"examples":[[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]]},"example":[{"property":"id","expression":"=","value":"webhook_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data ist standardmäßig eine nach öffentlichen IDs indizierte Sammlung, bei response_type=array eine Liste; limit enthält die Paginierung.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"webhook_012345abcdef_123456":{"id":"webhook_012345abcdef_123456","status":true,"url":"https://example.org/edoobox-hook","secret":false,"basicauth":false,"topics":["post_Booking"]}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"description":"Generic collection (ID-keyed object by default, array for response_type=array) or resource object. Structure fields are partial and action-dependent.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["webhook_012345abcdef_123456"]},"status":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"type":"boolean"},"url":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":48,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhookfunc.php","line":6,"reason":"Actual resource hooks validate URL for PUT and before POST.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"secret":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":62,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"basicauth":{"anyOf":[{"type":"string"},{"const":false}],"x-edoobox-sensitive":true,"description":"Empty stored text returns false. Nonempty text is processed by aQSec; at most 8 bytes are returned verbatim, longer values reveal first 5 and last 3 bytes with stars in between.","x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":84,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/helpers/edooboxhelp_helper.php","line":11583,"reason":"Followed aQSec: <=8 bytes unchanged, otherwise prefix/suffix partial mask.","sha256":"090d351b5bde81813c7a829bedf2f1f9f978fce5765f246989779f996198934e"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"date_create":{"anyOf":[{"type":"string","format":"date-time"},{"const":false}],"readOnly":true,"x-edoobox-contractSource":[{"file":"application/models/v2/Webhook.php","line":106,"reason":"Structure declaration: input get, output set, persistence flags and field hooks.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"application/models/v2/Webhook.php","line":162,"reason":"Resource POST/PUT dispatch reachability and preconditions; field contracts apply to parent resource, not sub-resource actions.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"},{"file":"docs/openapi/reviews/identity-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0928d996b9c89e19be93905970488d6b869ac62bcbc4b6f260cd13a5c385a055"}]},"topics":{"type":"array","items":{"type":"string"},"x-edoobox-contractSource":[{"file":"application/models/v2/Webhookfunc.php","line":24,"reason":"PUT consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":64,"reason":"Valid topics select an implemented model and post/put/delete.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhookfunc.php","line":95,"reason":"POST consumes an array of topic strings.","sha256":"9c9357f79d2bf15b855f0b105ec2f079e71d27dad3af7140964499ea644594f4"},{"file":"application/models/v2/Webhook.php","line":133,"reason":"Response splits joined topic names into a string array.","sha256":"cb224082d24ea3786aa8fc854251204ea410c53bb2b878b23a3cc2e614fe3fd1"}]}},"additionalProperties":true}}]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-sensitiveResponseFields":["secret"],"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/09-messages-ai.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Webhook.php","method":"getWebhook","line":212},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"webhook","short":"wbho","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/widget Design-Widget löschen Entfernt ein Widget aus dem Design, ordnet die verbleibenden Widgets neu und berechnet das Layout-CSS erneut. - Operation-ID: delete_v2_widget - Dokumentation: /docs/api/v2/operations/delete-v2-widget-8dbb9a84b39e/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | query | ja | Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"query","required":true,"schema":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]},"description":"Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_ID:?QUERY_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "widget_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/widget' \ --url-query 'id='"${QUERY_ID}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("id", required("QUERY_ID")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "widget_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("id", os.environ["QUERY_ID"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"widget_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_widget","summary":"Design-Widget löschen","description":"Entfernt ein Widget aus dem Design, ordnet die verbleibenden Widgets neu und berechnet das Layout-CSS erneut.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]},"description":"Öffentliche ID des zu löschenden Objekts; alternativ als id im JSON-Body."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete geben die angefragte Datei bzw. das Objekt und das Ergebnis an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"widget_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]}},"additionalProperties":true,"required":["id"]},"examples":{"request":{"summary":"Design-Widget löschen","value":{"id":"widget_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"deleteWidget","line":172},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/widget Design-Widget auflisten Liest konfigurierte Widgets einer Designvorlage samt Reihenfolge, HTML-ID und aufbereiteter config. config.data enthält die für den Widgettyp und das Konto gültigen Einstellungen. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. Zusätzlich ist ein filter-Eintrag mit property=template erforderlich, auch bei einer Pfad-ID; der separate template-Querywert wird nur von /list/LUT gelesen. - Operation-ID: get_v2_widget - Dokumentation: /docs/api/v2/operations/get-v2-widget-3e68627caf54/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | ja | Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"template","value":"a"},{"property":"design","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/widget' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_widget","summary":"Design-Widget auflisten","description":"Liest konfigurierte Widgets einer Designvorlage samt Reihenfolge, HTML-ID und aufbereiteter config. config.data enthält die für den Widgettyp und das Konto gültigen Einstellungen. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. Zusätzlich ist ein filter-Eintrag mit property=template erforderlich, auch bei einer Pfad-ID; der separate template-Querywert wird nur von /list/LUT gelesen.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"template","value":"a"},{"property":"design","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"widget_012345abcdef_123456":{"id":"widget_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","examples":["widget_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"template":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":52,"lineStart":52,"lineEnd":69,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"d125dfff1f7e616ba3a9089e77e4c41634b8a344f49db1807d72c020939f7e01","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"Before-insert validates selected template/key config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing and capability/Bootstrap filters."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"key":{"type":"string","description":"Native Structure setter returns aQ(widget_nr).","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":70,"lineEnd":82,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"Key getter exists, but POST acceptance additionally validates the original untruncated key against the exact runtime LUT; generic PUT marks key immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"order":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":84,"lineStart":84,"lineEnd":93,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"f6b8175dcaf1a404ef565db2fbe94467caba18bc5841242e61f417f1e096675d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":115,"lineStart":115,"lineEnd":136,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"523f2f60112cff2fd4e258096578e34669b9d5639e75fad58fe989b6b5b63425","reason":"func delegates to _putWidgetOrder"},{"file":"application/models/v2/Widgetfunc.php","line":766,"lineStart":766,"lineEnd":823,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"9adbee2fe3af3980de9e2c50bc82e91aaae3d60fbdfcfcc8499d64f9197e28dc","reason":"Actual reorder; no input range rejection."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"html_id":{"type":"string","description":"Native Structure setter returns aQ(widget_id), including the generated id.","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":94,"lineEnd":102,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"The input getter is superseded by before-insert generation; response setter uses aQ and generic PUT is immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"config":{"type":"object","properties":{"name":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"array","maxItems":0}]},"width":{"type":"object","additionalProperties":{}},"height":{"type":"object","additionalProperties":{}},"margin":{"type":"object","additionalProperties":{}},"visible":{"type":"object","properties":{"type":{"type":"integer"},"showpho":{"x-edoobox-canonicalType":"boolean","description":"Returned from persisted widget showpho directly; current writes normalize bool but legacy stored types are not recast here."}},"required":["type","showpho"],"additionalProperties":false}},"required":["name","data","width","height","margin","visible"],"additionalProperties":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","examples":["widget_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"template":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":52,"lineStart":52,"lineEnd":69,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"d125dfff1f7e616ba3a9089e77e4c41634b8a344f49db1807d72c020939f7e01","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"Before-insert validates selected template/key config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing and capability/Bootstrap filters."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"key":{"type":"string","description":"Native Structure setter returns aQ(widget_nr).","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":70,"lineEnd":82,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"Key getter exists, but POST acceptance additionally validates the original untruncated key against the exact runtime LUT; generic PUT marks key immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"order":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":84,"lineStart":84,"lineEnd":93,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"f6b8175dcaf1a404ef565db2fbe94467caba18bc5841242e61f417f1e096675d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":115,"lineStart":115,"lineEnd":136,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"523f2f60112cff2fd4e258096578e34669b9d5639e75fad58fe989b6b5b63425","reason":"func delegates to _putWidgetOrder"},{"file":"application/models/v2/Widgetfunc.php","line":766,"lineStart":766,"lineEnd":823,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"9adbee2fe3af3980de9e2c50bc82e91aaae3d60fbdfcfcc8499d64f9197e28dc","reason":"Actual reorder; no input range rejection."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"html_id":{"type":"string","description":"Native Structure setter returns aQ(widget_id), including the generated id.","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":94,"lineEnd":102,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"The input getter is superseded by before-insert generation; response setter uses aQ and generic PUT is immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"config":{"type":"object","properties":{"name":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"array","maxItems":0}]},"width":{"type":"object","additionalProperties":{}},"height":{"type":"object","additionalProperties":{}},"margin":{"type":"object","additionalProperties":{}},"visible":{"type":"object","properties":{"type":{"type":"integer"},"showpho":{"x-edoobox-canonicalType":"boolean","description":"Returned from persisted widget showpho directly; current writes normalize bool but legacy stored types are not recast here."}},"required":["type","showpho"],"additionalProperties":false}},"required":["name","data","width","height","margin","visible"],"additionalProperties":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"getWidget","line":213},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # POST /v2/widget Design-Widget anlegen Fügt ein Widget zu design und template hinzu. design, template, key und config sind erforderlich; key muss in der aktuellen Widget-LUT verfügbar sein. HTML-ID und Layout-CSS werden erzeugt. config.data ist ein offenes, vom Widgettyp und Konto abhängiges Einstellungsobjekt; unbekannte oder ungültige Einträge werden vom Handler abgewiesen. data akzeptiert einen Datensatz oder ein Array mit bis zu 100 Datensätzen; bei einem Batch enthält data.id ein Array öffentlicher IDs. - Operation-ID: post_v2_widget - Dokumentation: /docs/api/v2/operations/post-v2-widget-d396f4144e58/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "design": "design_012345abcdef_123456", "template": "a", "key": "textb", "config": { "name": "Hinweis" } } } EDOOBOX_JSON_EXAMPLE curl --request 'POST' \ --url "${API_ORIGIN%/}"'/v2/widget' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "design": "design_012345abcdef_123456", "template": "a", "key": "textb", "config": { "name": "Hinweis" } } }); const response = await fetch(url, { method: "POST", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"design\":\"design_012345abcdef_123456\",\"template\":\"a\",\"key\":\"textb\",\"config\":{\"name\":\"Hinweis\"}}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="POST", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"post_v2_widget","summary":"Design-Widget anlegen","description":"Fügt ein Widget zu design und template hinzu. design, template, key und config sind erforderlich; key muss in der aktuellen Widget-LUT verfügbar sein. HTML-ID und Layout-CSS werden erzeugt. config.data ist ein offenes, vom Widgettyp und Konto abhängiges Einstellungsobjekt; unbekannte oder ungültige Einträge werden vom Handler abgewiesen. data akzeptiert einen Datensatz oder ein Array mit bis zu 100 Datensätzen; bei einem Batch enthält data.id ein Array öffentlicher IDs.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data enthält insert, öffentliche id und bei Uploads signierte upload_url; öffentliche Dateien liefern zusätzlich cdn_url. Bei fehlgeschlagenem Transaktionsabschluss enthält data insert=false und id=null; error ist ED80100.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"widget_012345abcdef_123456","insert":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"insert":{"type":"boolean"},"id":{"oneOf":[{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]},{"type":"array","items":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]}},{"type":"null"}],"description":"Bei erfolgreichem Einzelinsert öffentliche v2-ID, bei erfolgreichem Batch ein Array öffentlicher IDs; bei fehlgeschlagenem Transaktionsabschluss null zusammen mit insert=false und error=ED80100."}},"additionalProperties":true,"required":["id","insert"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"type":"object","properties":{"id":false,"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38},"template":{"type":"string","enum":["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s"],"description":"Allowlist a..s; invalid strings returnfalse -> ED80100. Required create field, generic PUT immutable.\n\nNon-strict in_array allowlist then aP; manager chooses widget definitions for template/account/style.\n\nRuntime preconditions / rejection: Unknown string template -> ED80100. Widget key not offered for template/account/style -> missing_lut_widget.\n\nScope / limitations: Canonical string keys documented; non-strict in_array has unusual scalar-coercion edges, not a separate template API.","x-edoobox-schemaStatus":"source-reviewed","examples":["a"],"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":52,"lineStart":52,"lineEnd":69,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"d125dfff1f7e616ba3a9089e77e4c41634b8a344f49db1807d72c020939f7e01","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"Before-insert validates selected template/key config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing and capability/Bootstrap filters."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"key":{"type":"string","description":"Exact widget key from the current account/template/style Widgetmanager LUT. Native before-insert validates the original key; unknown keys fail ED80100 missing_lut_widget. This is not a free text field validated merely by a250-character storage normalizer. Read the widget LUT for currently available keys.","x-edoobox-validation":"array_key_exists(original key, Widgetmanager::getWidgetsTemplate(true, template, new, fid, styleId))","x-edoobox-schemaStatus":"source-reviewed-runtime-widget-lookup","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":70,"lineEnd":82,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"Key getter exists, but POST acceptance additionally validates the original untruncated key against the exact runtime LUT; generic PUT marks key immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":84,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"html_id":{"description":"Caller input is overwritten by the Widget before-insert hook. Native code generates wdg + original widget key + uniqid; supplied values do not select the stored HTML id. No caller-supplied250-character storage claim is made.","x-edoobox-ignored-input":true,"x-edoobox-schemaStatus":"source-reviewed-generated-field","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":94,"lineEnd":102,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"The input getter is superseded by before-insert generation; response setter uses aQ and generic PUT is immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"config":{"type":["object","array"],"properties":{"name":{"x-edoobox-canonicalType":"string","description":"aP(...,true,150), truncates rather than rejecting length."},"width":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"min":{"x-edoobox-canonicalType":"number","description":"Caller-supplied comparison minimum; not a fixed server validator."}},"additionalProperties":true},"height":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"min":{"x-edoobox-canonicalType":"number","description":"Caller-supplied comparison minimum."},"auto":{"x-edoobox-canonicalType":"integer","x-edoobox-recognizedValues":[1,2,3],"description":"Only cast values1/2/3 copied; other values ignored."}},"additionalProperties":true},"margin":{"type":"object","properties":{"left":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true},"top":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true}},"additionalProperties":true},"visible":{"type":"object","properties":{"showpho":{"x-edoobox-canonicalType":"boolean","description":"Only stricttrue enables; everything else becomes false."},"type":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true},"data":{"type":"object","additionalProperties":{},"description":"Widget-definition-specific keys and value forms. Select/checkbox/datastructure/default checks reject; input/integer/join/json/base64 forms normalize. Allowed keys depend on template/key/account languages/features/style.","x-edoobox-schemaStatus":"reviewed-dynamic-widget-map"}},"additionalProperties":true,"description":"Common widget layout/config envelope for POST/PUT. Unknown top-level config keys ignored; unknown data keys rejected. Nested numeric bounds mostly control copying, not rejection. The object is canonical; no explicit top-level is_array rejection is implemented. PHP JSON object/array values both decode to arrays; empty/non-keyed config arrays leave default/stored config unchanged.\n\nPOST get is placeholdertrue; before-insert calls validWidgetConfig. PUT calls same validator using existing stored config. Layout numbers cast/ignore invalid entries; names/inputs truncate. GET replaces empty Structure setter and converts per-field types after decode.\n\nRuntime preconditions / rejection: Missing template/key definition -> missing_lut_widget. Unknown data property or invalid key spelling -> config_data_field_not_exist. Select/checkbox/datastructure value not in dynamic definition -> config_data_field_value_invalid. Duplicate bformular participant-field binding -> config_data_field_already_linked. Invalid join default -> config_data_field_default_value_not_exist, except promo sale selection fallback. Promo-sale prices must normalize to at least one positive finite decimal, otherwise config_data_field_value_invalid.\n\nScope / limitations: Common envelope is resolved; full per-widget/LUT/account schema union is intentionally not enumerated. Dynamic data map remains permissive with explicit validation description. visibility_tags_include/exclude accepts arrays: documented form is {tag: publicv2ID}. Legacy scalar fallback casts internal IDs, which must never be used in external examples. bformular country/language defaults can transform public country IDs/language keys in their applicable branch. Runtime account languages, membership/Bootstrap filters and definition methods determine exact data keys; no single closed data object is proven.","x-edoobox-schemaStatus":"source-reviewed-dynamic-envelope","examples":[{"name":"Synthetic text block","visible":{"showpho":true,"type":1},"data":{"showiframe":"1","showsammel":"0"}}],"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["design","template","key","config"],"minProperties":1},{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","properties":{"id":false,"design":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"],"x-edoobox-writableOnUpdate":false,"x-edoobox-requiredOnCreate":true,"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":38},"template":{"type":"string","enum":["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s"],"description":"Allowlist a..s; invalid strings returnfalse -> ED80100. Required create field, generic PUT immutable.\n\nNon-strict in_array allowlist then aP; manager chooses widget definitions for template/account/style.\n\nRuntime preconditions / rejection: Unknown string template -> ED80100. Widget key not offered for template/account/style -> missing_lut_widget.\n\nScope / limitations: Canonical string keys documented; non-strict in_array has unusual scalar-coercion edges, not a separate template API.","x-edoobox-schemaStatus":"source-reviewed","examples":["a"],"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":52,"lineStart":52,"lineEnd":69,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"d125dfff1f7e616ba3a9089e77e4c41634b8a344f49db1807d72c020939f7e01","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"Before-insert validates selected template/key config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing and capability/Bootstrap filters."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]},"key":{"type":"string","description":"Exact widget key from the current account/template/style Widgetmanager LUT. Native before-insert validates the original key; unknown keys fail ED80100 missing_lut_widget. This is not a free text field validated merely by a250-character storage normalizer. Read the widget LUT for currently available keys.","x-edoobox-validation":"array_key_exists(original key, Widgetmanager::getWidgetsTemplate(true, template, new, fid, styleId))","x-edoobox-schemaStatus":"source-reviewed-runtime-widget-lookup","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":70,"lineEnd":82,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"Key getter exists, but POST acceptance additionally validates the original untruncated key against the exact runtime LUT; generic PUT marks key immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"order":{"x-edoobox-schemaStatus":"source-inferred","x-edoobox-sourceLine":84,"x-edoobox-canonicalType":"integer","x-edoobox-normalization":"PHP coercion/normalization is applied by the Structure getter; its return type is not a rejecting wire-input type restriction.","examples":[1]},"html_id":{"description":"Caller input is overwritten by the Widget before-insert hook. Native code generates wdg + original widget key + uniqid; supplied values do not select the stored HTML id. No caller-supplied250-character storage claim is made.","x-edoobox-ignored-input":true,"x-edoobox-schemaStatus":"source-reviewed-generated-field","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":94,"lineEnd":102,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"The input getter is superseded by before-insert generation; response setter uses aQ and generic PUT is immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"config":{"type":["object","array"],"properties":{"name":{"x-edoobox-canonicalType":"string","description":"aP(...,true,150), truncates rather than rejecting length."},"width":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"min":{"x-edoobox-canonicalType":"number","description":"Caller-supplied comparison minimum; not a fixed server validator."}},"additionalProperties":true},"height":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"min":{"x-edoobox-canonicalType":"number","description":"Caller-supplied comparison minimum."},"auto":{"x-edoobox-canonicalType":"integer","x-edoobox-recognizedValues":[1,2,3],"description":"Only cast values1/2/3 copied; other values ignored."}},"additionalProperties":true},"margin":{"type":"object","properties":{"left":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true},"top":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true}},"additionalProperties":true},"visible":{"type":"object","properties":{"showpho":{"x-edoobox-canonicalType":"boolean","description":"Only stricttrue enables; everything else becomes false."},"type":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true},"data":{"type":"object","additionalProperties":{},"description":"Widget-definition-specific keys and value forms. Select/checkbox/datastructure/default checks reject; input/integer/join/json/base64 forms normalize. Allowed keys depend on template/key/account languages/features/style.","x-edoobox-schemaStatus":"reviewed-dynamic-widget-map"}},"additionalProperties":true,"description":"Common widget layout/config envelope for POST/PUT. Unknown top-level config keys ignored; unknown data keys rejected. Nested numeric bounds mostly control copying, not rejection. The object is canonical; no explicit top-level is_array rejection is implemented. PHP JSON object/array values both decode to arrays; empty/non-keyed config arrays leave default/stored config unchanged.\n\nPOST get is placeholdertrue; before-insert calls validWidgetConfig. PUT calls same validator using existing stored config. Layout numbers cast/ignore invalid entries; names/inputs truncate. GET replaces empty Structure setter and converts per-field types after decode.\n\nRuntime preconditions / rejection: Missing template/key definition -> missing_lut_widget. Unknown data property or invalid key spelling -> config_data_field_not_exist. Select/checkbox/datastructure value not in dynamic definition -> config_data_field_value_invalid. Duplicate bformular participant-field binding -> config_data_field_already_linked. Invalid join default -> config_data_field_default_value_not_exist, except promo sale selection fallback. Promo-sale prices must normalize to at least one positive finite decimal, otherwise config_data_field_value_invalid.\n\nScope / limitations: Common envelope is resolved; full per-widget/LUT/account schema union is intentionally not enumerated. Dynamic data map remains permissive with explicit validation description. visibility_tags_include/exclude accepts arrays: documented form is {tag: publicv2ID}. Legacy scalar fallback casts internal IDs, which must never be used in external examples. bformular country/language defaults can transform public country IDs/language keys in their applicable branch. Runtime account languages, membership/Bootstrap filters and definition methods determine exact data keys; no single closed data object is proven.","x-edoobox-schemaStatus":"source-reviewed-dynamic-envelope","examples":[{"name":"Synthetic text block","visible":{"showpho":true,"type":1},"data":{"showiframe":"1","showsammel":"0"}}],"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"6b38123b776600a328c8e844b0ecd6e0a579144081a345e335400dad52a9a5db"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","required":["design","template","key","config"]}}],"x-edoobox-readOnlyFields":[],"x-edoobox-clientRestriction":"The server-generated id is forbidden. insert:false fields are readOnly and tolerated for existing Vue payloads because the API skips them."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Design-Widget anlegen","value":{"data":{"design":"design_012345abcdef_123456","template":"a","key":"textb","config":{"name":"Hinweis"}}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"postWidget","line":139},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[2,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/widget Design-Widget ändern Ändert Reihenfolge oder config eines bestehenden Widgets. design, template, key und html_id bleiben unveränderlich. Der Handler validiert Einstellungen anhand der aktuellen Widget-LUT und berechnet bei Layoutänderungen das Design-CSS neu. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_widget - Dokumentation: /docs/api/v2/operations/put-v2-widget-73ff8799dbd7/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "order": 2 }, "id": "widget_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/widget' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "order": 2 }, "id": "widget_012345abcdef_123456" }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget" url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"order\":2},\"id\":\"widget_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_widget","summary":"Design-Widget ändern","description":"Ändert Reihenfolge oder config eines bestehenden Widgets. design, template, key und html_id bleiben unveränderlich. Der Handler validiert Einstellungen anhand der aktuellen Widget-LUT und berechnet bei Layoutänderungen das Design-CSS neu. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"widget_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"design":false,"template":false,"key":false,"order":{"x-edoobox-canonicalType":"integer","description":"PHP int cast; integers are canonical. No rejecting wire type/range is checked; other JSON values also coerce.\n\nCast to int, reorder siblings, store returned position. No min/max input validation.\n\nRuntime preconditions / rejection: Missing widget -> callback false -> ED90100.\n\nScope / limitations: Do not claim minimum=1 or fixed maximum from normal output numbering.","x-edoobox-schemaStatus":"source-reviewed","examples":[2],"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":84,"lineStart":84,"lineEnd":93,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"f6b8175dcaf1a404ef565db2fbe94467caba18bc5841242e61f417f1e096675d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":115,"lineStart":115,"lineEnd":136,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"523f2f60112cff2fd4e258096578e34669b9d5639e75fad58fe989b6b5b63425","reason":"func delegates to _putWidgetOrder"},{"file":"application/models/v2/Widgetfunc.php","line":766,"lineStart":766,"lineEnd":823,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"9adbee2fe3af3980de9e2c50bc82e91aaae3d60fbdfcfcc8499d64f9197e28dc","reason":"Actual reorder; no input range rejection."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"html_id":false,"config":{"type":["object","array"],"properties":{"name":{"x-edoobox-canonicalType":"string","description":"aP(...,true,150), truncates rather than rejecting length."},"width":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"min":{"x-edoobox-canonicalType":"number","description":"Caller-supplied comparison minimum; not a fixed server validator."}},"additionalProperties":true},"height":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"min":{"x-edoobox-canonicalType":"number","description":"Caller-supplied comparison minimum."},"auto":{"x-edoobox-canonicalType":"integer","x-edoobox-recognizedValues":[1,2,3],"description":"Only cast values1/2/3 copied; other values ignored."}},"additionalProperties":true},"margin":{"type":"object","properties":{"left":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true},"top":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true}},"additionalProperties":true},"visible":{"type":"object","properties":{"showpho":{"x-edoobox-canonicalType":"boolean","description":"Only stricttrue enables; everything else becomes false."},"type":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true},"data":{"type":"object","additionalProperties":{},"description":"Widget-definition-specific keys and value forms. Select/checkbox/datastructure/default checks reject; input/integer/join/json/base64 forms normalize. Allowed keys depend on template/key/account languages/features/style.","x-edoobox-schemaStatus":"reviewed-dynamic-widget-map"}},"additionalProperties":true,"description":"Common widget layout/config envelope for POST/PUT. Unknown top-level config keys ignored; unknown data keys rejected. Nested numeric bounds mostly control copying, not rejection. The object is canonical; no explicit top-level is_array rejection is implemented. PHP JSON object/array values both decode to arrays; empty/non-keyed config arrays leave default/stored config unchanged.\n\nPOST get is placeholdertrue; before-insert calls validWidgetConfig. PUT calls same validator using existing stored config. Layout numbers cast/ignore invalid entries; names/inputs truncate. GET replaces empty Structure setter and converts per-field types after decode.\n\nRuntime preconditions / rejection: Missing template/key definition -> missing_lut_widget. Unknown data property or invalid key spelling -> config_data_field_not_exist. Select/checkbox/datastructure value not in dynamic definition -> config_data_field_value_invalid. Duplicate bformular participant-field binding -> config_data_field_already_linked. Invalid join default -> config_data_field_default_value_not_exist, except promo sale selection fallback. Promo-sale prices must normalize to at least one positive finite decimal, otherwise config_data_field_value_invalid.\n\nScope / limitations: Common envelope is resolved; full per-widget/LUT/account schema union is intentionally not enumerated. Dynamic data map remains permissive with explicit validation description. visibility_tags_include/exclude accepts arrays: documented form is {tag: publicv2ID}. Legacy scalar fallback casts internal IDs, which must never be used in external examples. bformular country/language defaults can transform public country IDs/language keys in their applicable branch. Runtime account languages, membership/Bootstrap filters and definition methods determine exact data keys; no single closed data object is proven.","x-edoobox-schemaStatus":"source-reviewed-dynamic-envelope","examples":[{"name":"Renamed synthetic widget"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["design","template","key","html_id"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]}},"additionalProperties":true,"required":["data","id"]},"examples":{"request":{"summary":"Design-Widget ändern","value":{"data":{"order":2},"id":"widget_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"updateWidget","line":156},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # DELETE /v2/widget/{id} Design-Widget löschen Entfernt ein Widget aus dem Design, ordnet die verbleibenden Widgets neu und berechnet das Layout-CSS erneut. - Operation-ID: delete_v2_widget_id - Dokumentation: /docs/api/v2/operations/delete-v2-widget-id-ad9e269c8b79/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "id": "widget_012345abcdef_123456" } EDOOBOX_JSON_EXAMPLE curl --request 'DELETE' \ --url "${API_ORIGIN%/}"'/v2/widget/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "id": "widget_012345abcdef_123456" }); const response = await fetch(url, { method: "DELETE", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"id\":\"widget_012345abcdef_123456\"}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="DELETE", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"delete_v2_widget_id","summary":"Design-Widget löschen","description":"Entfernt ein Widget aus dem Design, ordnet die verbleibenden Widgets neu und berechnet das Layout-CSS erneut.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id und delete geben die angefragte Datei bzw. das Objekt und das Ergebnis an.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"widget_012345abcdef_123456","delete":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]},"delete":{"type":"boolean"}},"additionalProperties":true,"required":["id","delete"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},"additionalProperties":true,"description":"Öffentliche ID des zu löschenden Datensatzes. Bei einer ID im Pfad ist kein JSON-Body erforderlich; zusätzliche Create-/Update-Felder werden beim Löschen nicht verarbeitet."},"examples":{"command":{"summary":"Synthetic request envelope; use fields documented by the resource.","value":{"id":"widget_012345abcdef_123456"}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"deleteWidget","line":172},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/widget/{id} Design-Widget lesen Liest konfigurierte Widgets einer Designvorlage samt Reihenfolge, HTML-ID und aufbereiteter config. config.data enthält die für den Widgettyp und das Konto gültigen Einstellungen. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. Zusätzlich ist ein filter-Eintrag mit property=template erforderlich, auch bei einer Pfad-ID; der separate template-Querywert wird nur von /list/LUT gelesen. - Operation-ID: get_v2_widget_id - Dokumentation: /docs/api/v2/operations/get-v2-widget-id-bc52621aec74/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | ja | Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"template","value":"a"},{"property":"design","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/widget/'"${PATH_ID}"'' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_widget_id","summary":"Design-Widget lesen","description":"Liest konfigurierte Widgets einer Designvorlage samt Reihenfolge, HTML-ID und aufbereiteter config. config.data enthält die für den Widgettyp und das Konto gültigen Einstellungen. Die Pfad-ID begrenzt die Auswahl auf dieses Objekt. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. Zusätzlich ist ein filter-Eintrag mit property=template erforderlich, auch bei einer Pfad-ID; der separate template-Querywert wird nur von /list/LUT gelesen.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"template","value":"a"},{"property":"design","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"widget_012345abcdef_123456":{"id":"widget_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","examples":["widget_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"template":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":52,"lineStart":52,"lineEnd":69,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"d125dfff1f7e616ba3a9089e77e4c41634b8a344f49db1807d72c020939f7e01","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"Before-insert validates selected template/key config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing and capability/Bootstrap filters."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"key":{"type":"string","description":"Native Structure setter returns aQ(widget_nr).","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":70,"lineEnd":82,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"Key getter exists, but POST acceptance additionally validates the original untruncated key against the exact runtime LUT; generic PUT marks key immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"order":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":84,"lineStart":84,"lineEnd":93,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"f6b8175dcaf1a404ef565db2fbe94467caba18bc5841242e61f417f1e096675d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":115,"lineStart":115,"lineEnd":136,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"523f2f60112cff2fd4e258096578e34669b9d5639e75fad58fe989b6b5b63425","reason":"func delegates to _putWidgetOrder"},{"file":"application/models/v2/Widgetfunc.php","line":766,"lineStart":766,"lineEnd":823,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"9adbee2fe3af3980de9e2c50bc82e91aaae3d60fbdfcfcc8499d64f9197e28dc","reason":"Actual reorder; no input range rejection."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"html_id":{"type":"string","description":"Native Structure setter returns aQ(widget_id), including the generated id.","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":94,"lineEnd":102,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"The input getter is superseded by before-insert generation; response setter uses aQ and generic PUT is immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"config":{"type":"object","properties":{"name":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"array","maxItems":0}]},"width":{"type":"object","additionalProperties":{}},"height":{"type":"object","additionalProperties":{}},"margin":{"type":"object","additionalProperties":{}},"visible":{"type":"object","properties":{"type":{"type":"integer"},"showpho":{"x-edoobox-canonicalType":"boolean","description":"Returned from persisted widget showpho directly; current writes normalize bool but legacy stored types are not recast here."}},"required":["type","showpho"],"additionalProperties":false}},"required":["name","data","width","height","margin","visible"],"additionalProperties":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","examples":["widget_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"template":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":52,"lineStart":52,"lineEnd":69,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"d125dfff1f7e616ba3a9089e77e4c41634b8a344f49db1807d72c020939f7e01","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"Before-insert validates selected template/key config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing and capability/Bootstrap filters."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"key":{"type":"string","description":"Native Structure setter returns aQ(widget_nr).","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":70,"lineEnd":82,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"Key getter exists, but POST acceptance additionally validates the original untruncated key against the exact runtime LUT; generic PUT marks key immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"order":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":84,"lineStart":84,"lineEnd":93,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"f6b8175dcaf1a404ef565db2fbe94467caba18bc5841242e61f417f1e096675d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":115,"lineStart":115,"lineEnd":136,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"523f2f60112cff2fd4e258096578e34669b9d5639e75fad58fe989b6b5b63425","reason":"func delegates to _putWidgetOrder"},{"file":"application/models/v2/Widgetfunc.php","line":766,"lineStart":766,"lineEnd":823,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"9adbee2fe3af3980de9e2c50bc82e91aaae3d60fbdfcfcc8499d64f9197e28dc","reason":"Actual reorder; no input range rejection."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"html_id":{"type":"string","description":"Native Structure setter returns aQ(widget_id), including the generated id.","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":94,"lineEnd":102,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"The input getter is superseded by before-insert generation; response setter uses aQ and generic PUT is immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"config":{"type":"object","properties":{"name":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"array","maxItems":0}]},"width":{"type":"object","additionalProperties":{}},"height":{"type":"object","additionalProperties":{}},"margin":{"type":"object","additionalProperties":{}},"visible":{"type":"object","properties":{"type":{"type":"integer"},"showpho":{"x-edoobox-canonicalType":"boolean","description":"Returned from persisted widget showpho directly; current writes normalize bool but legacy stored types are not recast here."}},"required":["type","showpho"],"additionalProperties":false}},"required":["name","data","width","height","margin","visible"],"additionalProperties":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"getWidget","line":213},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # PUT /v2/widget/{id} Design-Widget ändern Ändert Reihenfolge oder config eines bestehenden Widgets. design, template, key und html_id bleiben unveränderlich. Der Handler validiert Einstellungen anhand der aktuellen Widget-LUT und berechnet bei Layoutänderungen das Design-CSS neu. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich. - Operation-ID: put_v2_widget_id - Dokumentation: /docs/api/v2/operations/put-v2-widget-id-2887eb660c34/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: ja - Für Automation freigegeben: nein ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | id | path | ja | v2 object identifier in the selected account. | | language | header | nein | Account language code, where the resource supports setLanguage(). | | X-Edoobox-If-Revision | header | nein | Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review. | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. # cURL: PATH_* bereits URL-kodiert setzen. JavaScript/Python kodieren die unveränderten Pfadwerte selbst. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${PATH_ID:?PATH_ID setzen}" cat > payload.json <<'EDOOBOX_JSON_EXAMPLE' { "data": { "order": 2 } } EDOOBOX_JSON_EXAMPLE curl --request 'PUT' \ --url "${API_ORIGIN%/}"'/v2/widget/'"${PATH_ID}"'' \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" \ --header 'Content-Type: application/json' \ --data-binary '@payload.json' ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget/{id}"; path = path.replaceAll("{id}", encodeURIComponent(required("PATH_ID"))); const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); headers['Content-Type'] = "application/json"; const body = JSON.stringify({ "data": { "order": 2 } }); const response = await fetch(url, { method: "PUT", headers, body, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # Request Body: synthetisches Beispiel aus der Spezifikation; Werte und Vollständigkeit vor Verwendung prüfen. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget/{id}" path = path.replace("{id}", urllib.parse.quote(os.environ["PATH_ID"], safe="")) url = os.environ["API_ORIGIN"].rstrip("/") + path headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] headers["Content-Type"] = "application/json" body = "{\"data\":{\"order\":2}}".encode("utf-8") # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="PUT", data=body) try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"put_v2_widget_id","summary":"Design-Widget ändern","description":"Ändert Reihenfolge oder config eines bestehenden Widgets. design, template, key und html_id bleiben unveränderlich. Der Handler validiert Einstellungen anhand der aktuellen Widget-LUT und berechnet bei Layoutänderungen das Design-CSS neu. Ein nicht leeres data mit mindestens einem unterstützten Änderungsfeld ist erforderlich.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"v2 object identifier in the selected account.","schema":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}},{"name":"X-Edoobox-If-Revision","in":"header","required":false,"schema":{"type":"string","pattern":"^[a-f0-9]{32}$","examples":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]},"description":"Optional account mutation revision from a preceding authenticated auth-context snapshot. The dispatcher compares it while holding the account lock; mismatch409 requires a new review."}],"responses":{"200":{"description":"data.id bezeichnet das Objekt, data.update zeigt an, ob sich ein Feld geändert hat.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"id":"widget_012345abcdef_123456","update":true}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID. Beispiel ist synthetisch.","examples":["widget_012345abcdef_123456"]},"update":{"type":"boolean"}},"additionalProperties":true,"required":["id","update"]},"type":{"type":"string"}},"additionalProperties":true}}},"headers":{"X-Edoobox-Revision":{"schema":{"type":"string","pattern":"^[a-f0-9]{32}$"},"description":"Rotated account revision supplied for the next action in one reviewed sequence."}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"409":{"description":"The account changed after the reviewed read. No domain operation is dispatched; obtain fresh state and review again.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED40901","error_code":"mutation_revision_conflict"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}},"503":{"description":"The account mutation lock or revision store is unavailable. No unprotected fallback is executed.","headers":{"Retry-After":{"schema":{"type":"integer","const":1}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"error":"ED50301","error_code":"mutation_revision_busy"}}}}},"x-edoobox-mutates-state":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":false,"design":false,"template":false,"key":false,"order":{"x-edoobox-canonicalType":"integer","description":"PHP int cast; integers are canonical. No rejecting wire type/range is checked; other JSON values also coerce.\n\nCast to int, reorder siblings, store returned position. No min/max input validation.\n\nRuntime preconditions / rejection: Missing widget -> callback false -> ED90100.\n\nScope / limitations: Do not claim minimum=1 or fixed maximum from normal output numbering.","x-edoobox-schemaStatus":"source-reviewed","examples":[2],"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":84,"lineStart":84,"lineEnd":93,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"f6b8175dcaf1a404ef565db2fbe94467caba18bc5841242e61f417f1e096675d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":115,"lineStart":115,"lineEnd":136,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"523f2f60112cff2fd4e258096578e34669b9d5639e75fad58fe989b6b5b63425","reason":"func delegates to _putWidgetOrder"},{"file":"application/models/v2/Widgetfunc.php","line":766,"lineStart":766,"lineEnd":823,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"9adbee2fe3af3980de9e2c50bc82e91aaae3d60fbdfcfcc8499d64f9197e28dc","reason":"Actual reorder; no input range rejection."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"html_id":false,"config":{"type":["object","array"],"properties":{"name":{"x-edoobox-canonicalType":"string","description":"aP(...,true,150), truncates rather than rejecting length."},"width":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"min":{"x-edoobox-canonicalType":"number","description":"Caller-supplied comparison minimum; not a fixed server validator."}},"additionalProperties":true},"height":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"min":{"x-edoobox-canonicalType":"number","description":"Caller-supplied comparison minimum."},"auto":{"x-edoobox-canonicalType":"integer","x-edoobox-recognizedValues":[1,2,3],"description":"Only cast values1/2/3 copied; other values ignored."}},"additionalProperties":true},"margin":{"type":"object","properties":{"left":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true},"top":{"type":"object","properties":{"setpc":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"settab":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."},"setpho":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true}},"additionalProperties":true},"visible":{"type":"object","properties":{"showpho":{"x-edoobox-canonicalType":"boolean","description":"Only stricttrue enables; everything else becomes false."},"type":{"x-edoobox-canonicalType":"integer","description":"PHP int normalization, not strict rejecting wire numeric restriction."}},"additionalProperties":true},"data":{"type":"object","additionalProperties":{},"description":"Widget-definition-specific keys and value forms. Select/checkbox/datastructure/default checks reject; input/integer/join/json/base64 forms normalize. Allowed keys depend on template/key/account languages/features/style.","x-edoobox-schemaStatus":"reviewed-dynamic-widget-map"}},"additionalProperties":true,"description":"Common widget layout/config envelope for POST/PUT. Unknown top-level config keys ignored; unknown data keys rejected. Nested numeric bounds mostly control copying, not rejection. The object is canonical; no explicit top-level is_array rejection is implemented. PHP JSON object/array values both decode to arrays; empty/non-keyed config arrays leave default/stored config unchanged.\n\nPOST get is placeholdertrue; before-insert calls validWidgetConfig. PUT calls same validator using existing stored config. Layout numbers cast/ignore invalid entries; names/inputs truncate. GET replaces empty Structure setter and converts per-field types after decode.\n\nRuntime preconditions / rejection: Missing template/key definition -> missing_lut_widget. Unknown data property or invalid key spelling -> config_data_field_not_exist. Select/checkbox/datastructure value not in dynamic definition -> config_data_field_value_invalid. Duplicate bformular participant-field binding -> config_data_field_already_linked. Invalid join default -> config_data_field_default_value_not_exist, except promo sale selection fallback. Promo-sale prices must normalize to at least one positive finite decimal, otherwise config_data_field_value_invalid.\n\nScope / limitations: Common envelope is resolved; full per-widget/LUT/account schema union is intentionally not enumerated. Dynamic data map remains permissive with explicit validation description. visibility_tags_include/exclude accepts arrays: documented form is {tag: publicv2ID}. Legacy scalar fallback casts internal IDs, which must never be used in external examples. bformular country/language defaults can transform public country IDs/language keys in their applicable branch. Runtime account languages, membership/Bootstrap filters and definition methods determine exact data keys; no single closed data object is proven.","x-edoobox-schemaStatus":"source-reviewed-dynamic-envelope","examples":[{"name":"Renamed synthetic widget"}],"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"description":"Source-declared fields; action-specific and account validation still apply.","minProperties":1,"x-edoobox-readOnlyFields":["design","template","key","html_id"],"x-edoobox-clientRestriction":"Read-only fields are forbidden by this client contract to prevent silently ignored writes; the legacy API itself skips them. Explicit source-marked update context fields remain accepted for hook validation without changing their immutable database columns."},"id":{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["widget_012345abcdef_123456"]}},"additionalProperties":true,"required":["data"]},"examples":{"request":{"summary":"Design-Widget ändern","value":{"data":{"order":2}}}}}}},"x-edoobox-runtimeMutationGuard":true,"x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"updateWidget","line":156},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":false,"x-edoobox-safeForAutomation":false,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/widget/list Design-Widget auflisten Liest konfigurierte Widgets einer Designvorlage samt Reihenfolge, HTML-ID und aufbereiteter config. config.data enthält die für den Widgettyp und das Konto gültigen Einstellungen. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. Zusätzlich ist ein filter-Eintrag mit property=template erforderlich, auch bei einer Pfad-ID; der separate template-Querywert wird nur von /list/LUT gelesen. - Operation-ID: get_v2_widget_list - Dokumentation: /docs/api/v2/operations/get-v2-widget-list-902a366ee427/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | fields | query | nein | JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | filter | query | ja | Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen. | | limit | query | nein | JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | order | query | nein | JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it. | | response_type | query | nein | Controls generic collection representation; default is an ID-keyed object. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"template","value":"a"},{"property":"design","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_FILTER:?QUERY_FILTER setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/widget/list' \ --url-query 'filter='"${QUERY_FILTER}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. // filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget/list"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("filter", required("QUERY_FILTER")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. # filter: komplexen Wert vorab gemäss content/style/explode serialisieren; das Beispiel verwendet einen vorbereiteten Wert. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget/list" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("filter", os.environ["QUERY_FILTER"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_widget_list","summary":"Design-Widget auflisten","description":"Liest konfigurierte Widgets einer Designvorlage samt Reihenfolge, HTML-ID und aufbereiteter config. config.data enthält die für den Widgettyp und das Konto gültigen Einstellungen. Ohne ID wird eine gefilterte und paginierte Sammlung zurückgegeben. fields, filter, order und limit steuern die Auswahl; data ist standardmäßig nach v2-ID indiziert oder mit response_type=array eine Liste. Zusätzlich ist ein filter-Eintrag mit property=template erforderlich, auch bei einer Pfad-ID; der separate template-Querywert wird nur von /list/LUT gelesen.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"fields","in":"query","required":false,"description":"JSON-encoded fields value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"examples":[["id"]]},"example":["id"]}}},{"name":"filter","in":"query","required":true,"description":"Erforderlich: ein template-Filter mit einem gültigen Vorlagenschlüssel a bis s; design kann die Sammlung auf ein Buchungsdesign begrenzen.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{}},"additionalProperties":true,"required":["property","value"]}},"example":[{"property":"template","value":"a"},{"property":"design","value":"design_012345abcdef_123456"}]}}},{"name":"limit","in":"query","required":false,"description":"JSON-encoded limit value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"default":0},"reply":{"type":"integer","maximum":2000,"default":100}},"examples":[{"start":0,"reply":25}]},"example":{"start":0,"reply":25}}}},{"name":"order","in":"query","required":false,"description":"JSON-encoded order value consumed by RequestAPIData / API_DATA; specific actions may ignore it.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"value":{"type":"string","enum":["ASC","DESC"]}},"additionalProperties":true},"examples":[[{"property":"id","value":"ASC"}]]},"example":[{"property":"id","value":"ASC"}]}}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["array","object"],"examples":["array"]},"description":"Controls generic collection representation; default is an ID-keyed object."},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"Sammlung ausgewählter Felder in data; zusätzlich limit mit start, reply und total.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"widget_012345abcdef_123456":{"id":"widget_012345abcdef_123456"}},"limit":{"start":0,"reply":100,"total":1}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"oneOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","examples":["widget_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"template":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":52,"lineStart":52,"lineEnd":69,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"d125dfff1f7e616ba3a9089e77e4c41634b8a344f49db1807d72c020939f7e01","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"Before-insert validates selected template/key config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing and capability/Bootstrap filters."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"key":{"type":"string","description":"Native Structure setter returns aQ(widget_nr).","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":70,"lineEnd":82,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"Key getter exists, but POST acceptance additionally validates the original untruncated key against the exact runtime LUT; generic PUT marks key immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"order":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":84,"lineStart":84,"lineEnd":93,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"f6b8175dcaf1a404ef565db2fbe94467caba18bc5841242e61f417f1e096675d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":115,"lineStart":115,"lineEnd":136,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"523f2f60112cff2fd4e258096578e34669b9d5639e75fad58fe989b6b5b63425","reason":"func delegates to _putWidgetOrder"},{"file":"application/models/v2/Widgetfunc.php","line":766,"lineStart":766,"lineEnd":823,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"9adbee2fe3af3980de9e2c50bc82e91aaae3d60fbdfcfcc8499d64f9197e28dc","reason":"Actual reorder; no input range rejection."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"html_id":{"type":"string","description":"Native Structure setter returns aQ(widget_id), including the generated id.","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":94,"lineEnd":102,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"The input getter is superseded by before-insert generation; response setter uses aQ and generic PUT is immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"config":{"type":"object","properties":{"name":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"array","maxItems":0}]},"width":{"type":"object","additionalProperties":{}},"height":{"type":"object","additionalProperties":{}},"margin":{"type":"object","additionalProperties":{}},"visible":{"type":"object","properties":{"type":{"type":"integer"},"showpho":{"x-edoobox-canonicalType":"boolean","description":"Returned from persisted widget showpho directly; current writes normalize bool but legacy stored types are not recast here."}},"required":["type","showpho"],"additionalProperties":false}},"required":["name","data","width","height","margin","visible"],"additionalProperties":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^widget_[a-f0-9]{12}_-?[0-9]+$","examples":["widget_012345abcdef_123456"],"description":"Öffentliche v2-ID aus diesem Konto; keine Datenbank-ID."},"design":{"description":"Response field from the resource Structure; dynamic values remain unconstrained.","x-edoobox-sourceLine":38,"anyOf":[{"type":"string","pattern":"^[a-z0-9]+_[a-f0-9]{12}_-?[0-9]+$","description":"Opaque edoobox v2 object ID returned by this account. Never send a raw database ID. The synthetic example is illustrative and is not a real record.","examples":["design_012345abcdef_123456"]},{"const":false},{"type":"null"}]},"template":{"type":"string","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":52,"lineStart":52,"lineEnd":69,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"d125dfff1f7e616ba3a9089e77e4c41634b8a344f49db1807d72c020939f7e01","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"Before-insert validates selected template/key config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing and capability/Bootstrap filters."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"key":{"type":"string","description":"Native Structure setter returns aQ(widget_nr).","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":70,"lineEnd":82,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"Key getter exists, but POST acceptance additionally validates the original untruncated key against the exact runtime LUT; generic PUT marks key immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"order":{"type":"integer","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":84,"lineStart":84,"lineEnd":93,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"f6b8175dcaf1a404ef565db2fbe94467caba18bc5841242e61f417f1e096675d","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":115,"lineStart":115,"lineEnd":136,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"523f2f60112cff2fd4e258096578e34669b9d5639e75fad58fe989b6b5b63425","reason":"func delegates to _putWidgetOrder"},{"file":"application/models/v2/Widgetfunc.php","line":766,"lineStart":766,"lineEnd":823,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"9adbee2fe3af3980de9e2c50bc82e91aaae3d60fbdfcfcc8499d64f9197e28dc","reason":"Actual reorder; no input range rejection."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]},"html_id":{"type":"string","description":"Native Structure setter returns aQ(widget_id), including the generated id.","x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":94,"lineEnd":102,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"The input getter is superseded by before-insert generation; response setter uses aQ and generic PUT is immutable."},{"file":"application/models/v2/Widget.php","line":139,"lineEnd":145,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","reason":"POST uses the generic insert pipeline with the actual Widget before-insert hook."},{"file":"application/models/v2/API_DATA.php","line":1341,"lineEnd":1352,"sha256":"c0e15d26a7a4f3fb3c25e35557d4420ad395b2af3925caec292de08e32ebf44c","reason":"Single insert runs the concrete insert_func_before result immediately before the query-builder insert."},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineEnd":113,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"Always generates widget_id as wdg + original key + uniqid, then validates the original key and config."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineEnd":230,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","reason":"The actual key must exist in the account/template/style-specific Widgetmanager result; missing LUT key throws ED80100."},{"file":"application/models/Widgetmanager.php","line":543,"lineEnd":689,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","reason":"Builds template-specific and common widget LUTs and applies account membership and Bootstrap53/style filters. Allowed keys are runtime-dependent, not arbitrary length-limited text."},{"file":"docs/openapi/reviews/widget-insert-fields.json","line":1,"sha256":"4bc713549d08e5eb8c5a027face2bb80313a1a8ec470dcb2bb0d957d8d061215","reason":"Independent supplemental field review; excluded from the historical184-field baseline count."}]},"config":{"type":"object","properties":{"name":{"type":"string"},"data":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"array","maxItems":0}]},"width":{"type":"object","additionalProperties":{}},"height":{"type":"object","additionalProperties":{}},"margin":{"type":"object","additionalProperties":{}},"visible":{"type":"object","properties":{"type":{"type":"integer"},"showpho":{"x-edoobox-canonicalType":"boolean","description":"Returned from persisted widget showpho directly; current writes normalize bool but legacy stored types are not recast here."}},"required":["type","showpho"],"additionalProperties":false}},"required":["name","data","width","height","margin","visible"],"additionalProperties":false,"x-edoobox-contractSource":[{"file":"application/models/v2/Widget.php","line":104,"lineStart":104,"lineEnd":116,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"8c74e6f4c65610843796bd8bb20351c22fab3f754a8c80b28294eab9bd1b6fa9","reason":"Structure getter, output setter and field flags."},{"file":"application/models/v2/Widgetfunc.php","line":151,"lineStart":151,"lineEnd":196,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"284bae8394c5dfde4b955795281105d8eb14e50ca5b9eb0a858250050ae921f5","reason":"func delegates to _putWidgetConfig"},{"file":"application/models/v2/Widgetfunc.php","line":107,"lineStart":107,"lineEnd":112,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"f4e1a72ce7b8315585fac1f8865c7c8e095d07a2bf68913cd6871947bc4fed3e","reason":"POST before-insert delegates to config validator."},{"file":"application/models/v2/Widgetfunc.php","line":210,"lineStart":210,"lineEnd":578,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"4256de6c9e572438e9ee93564e1c0fd3e3de8130a46717bbdbd9d5bbc6dd2954","reason":"Complete config validator: envelope copying, dynamic data dispatch, rejection, normalization and persistence."},{"file":"application/models/v2/Widgetfunc.php","line":13,"lineStart":13,"lineEnd":38,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"cc205b761bed5fe381e50ce4ea4852f48d4c71fab9b9024aa5d694c05727ecbc","reason":"Visibility-tag public-ID objects and legacy raw-ID fallback; publish only public IDs."},{"file":"application/models/v2/Widgetfunc.php","line":580,"lineStart":580,"lineEnd":603,"sha256":"93543ac4d3f60d425ce4e63837a1ca1dc9ed57baad776ccb7b76d448d475837a","spanSha256":"c1ece99a73d47b79be3af4614b18c25a2a6da2b8c906819f48112a38ad1b9db2","reason":"Promo price normalization to positive finite decimals."},{"file":"application/models/v2/Widget.php","line":213,"lineStart":213,"lineEnd":329,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"9fdfd0cde04c0a48fa910b0451939838cce8d6946711064e013fc4f8588389ce","reason":"Actual GET replaces empty declared setter and returns normalized config."},{"file":"application/models/Widgetmanager.php","line":543,"lineStart":543,"lineEnd":694,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"1fec1424aa25e05a811140d656c1955b890bbb6db6b23ffcb7e318c394fc402c","reason":"Template routing, capabilities and Bootstrap filtering determine dynamic definitions."},{"file":"application/models/Widgetmanager.php","line":7282,"lineStart":7282,"lineEnd":7380,"sha256":"f1350720149701f6749556a0af52a5979d371c0a845df25a76852343a0f95c48","spanSha256":"6c1613bbc268dca6e138f967dcefca91af35b6f5400bfdaa2c5f9ff7d19379f7","reason":"Concrete textb definition proves example keys and language-dependent inputs."},{"file":"application/models/v2/Widget.php","line":139,"lineStart":139,"lineEnd":162,"sha256":"425ea689bf11c52a3ab73e5f4c44acf48b4b6d860a7b88193e3d24b891273cde","spanSha256":"c2cae8eb38a7f944da1d9568bbcb3ed1f5e39fdc42507ec188464bb4bd5ab5b5","reason":"Actual resource dispatch and generic writer reachability; action branches are distinct."},{"file":"docs/openapi/reviews/domain-fields.json","line":1,"reason":"Independent field-level source review, direction applicability, examples and explicit runtime limitations.","sha256":"0da1cbbf108a5871b54204c3c76aaf59dabf85a6886bfb38210136885cfc1667"}]}},"additionalProperties":true,"required":["id"]}}],"description":"data ist standardmäßig nach öffentlicher ID indiziert; response_type=array liefert eine Liste. Eine leere Sammlung kann [] sein."},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"getWidget","line":213},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ``` --- # GET /v2/widget/list/LUT Verfügbare Widgettypen für eine Seitenvorlage lesen Liefert die im Konto für template verfügbaren Widgettypen mit Namen, Struktur, Layoutvorgaben und sichtbaren Einstellungen. Der separate Queryparameter template ist erforderlich. Die Antwort dient zur Wahl von key und zur Validierung von config.data bei POST/PUT widget; angebotene Typen hängen von Vorlage und Kontofunktionen ab. - Operation-ID: get_v2_widget_list_LUT - Dokumentation: /docs/api/v2/operations/get-v2-widget-list-lut-369caa13f8fe/ - OpenAPI-Version: 3.2.1 - Vollständige Spezifikation: /docs/api/v2/openapi.json - Dokumentationsstatus: reviewed - Ändert Zustand: nein, gemäss Annotation - Für Automation freigegeben: ja, siehe Voraussetzungen ## Server Serverwerte stammen unverändert aus der Datei. Eine lokale Entwicklungsadresse ist keine bestätigte Produktionsadresse. `API_ORIGIN` muss auf die passende Instanz gesetzt werden. ```json [{"url":"https://app2.edoobox.com","description":"edoobox API"}] ``` ## Authentifizierung Eine der folgenden Alternativen genügt (ODER). Innerhalb einer Alternative müssen alle Verfahren gemeinsam erfüllt sein (UND). 1. **bearerAuth** (HTTP bearer) — Existing edoobox v2 access token. Never substitute API key/secret headers. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. 2. **sessionCookie** (cookie: _edlogin) — Existing browser login session, with the same permission checks as Bearer authentication. **UND** **accountContext** (header: edid) — Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account. ### Wirksame Security Requirements und vollständige Verfahren ```json {"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Existing edoobox v2 access token. Never substitute API key/secret headers."},"accountContext":{"type":"apiKey","in":"header","name":"edid","description":"Selected login/account context from the JWT, not a database primary key. Required even for a JWT with one account."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"_edlogin","description":"Existing browser login session, with the same permission checks as Bearer authentication."}}} ``` ## Parameter | Name | Ort | Pflicht | Beschreibung | | --- | --- | --- | --- | | template | query | ja | Seitenvorlagenschlüssel aus /design/list/templates. | | language | header | nein | Account language code, where the resource supports setLanguage(). | ### Vollständige wirksame Parameterdefinitionen ```json [{"name":"template","in":"query","required":true,"description":"Seitenvorlagenschlüssel aus /design/list/templates.","schema":{"type":"string","enum":["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s"],"examples":["a"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}] ``` ## Aufrufbeispiele Anpassen vor dem Ausführen. Platzhalter und veröffentlichte Beispiele sind synthetisch. Die Beispiele senden hier keine Anfrage; sie ergänzen keine unbekannten Pflichtfelder. ### cURL ```sh # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. : "${API_ORIGIN:?API_ORIGIN setzen}" : "${AUTH_BEARERAUTH:?AUTH_BEARERAUTH setzen}" : "${AUTH_ACCOUNTCONTEXT:?AUTH_ACCOUNTCONTEXT setzen}" : "${QUERY_TEMPLATE:?QUERY_TEMPLATE setzen}" curl --request 'GET' \ --url "${API_ORIGIN%/}"'/v2/widget/list/LUT' \ --url-query 'template='"${QUERY_TEMPLATE}" \ --header 'Authorization: Bearer '"${AUTH_BEARERAUTH}" \ --header 'edid: '"${AUTH_ACCOUNTCONTEXT}" ``` ### JavaScript (Node.js) ```javascript // Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. const required = (name) => { const value = process.env[name]; if (!value) throw new Error(`Set ${name}`); return value; }; let path = "/v2/widget/list/LUT"; const origin = required('API_ORIGIN'); const url = new URL((origin.endsWith('/') ? origin.slice(0, -1) : origin) + path); url.searchParams.append("template", required("QUERY_TEMPLATE")); const headers = {}; headers["Authorization"] = "Bearer " + required("AUTH_BEARERAUTH"); headers["edid"] = "" + required("AUTH_ACCOUNTCONTEXT"); const response = await fetch(url, { method: "GET", headers, redirect: 'manual' }); const text = await response.text(); console.log(response.status, text); // HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ### Python ```python # Zeigt Authentifizierungsalternative 1; alle Verfahren dieser Alternative sind gemeinsam erforderlich. import os import urllib.parse import urllib.request import urllib.error path = "/v2/widget/list/LUT" url = os.environ["API_ORIGIN"].rstrip("/") + path url += "?" + urllib.parse.urlencode([("template", os.environ["QUERY_TEMPLATE"])]) headers = {} headers["Authorization"] = "Bearer " + os.environ["AUTH_BEARERAUTH"] headers["edid"] = "" + os.environ["AUTH_ACCOUNTCONTEXT"] # Redirects nicht automatisch verfolgen. class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None request = urllib.request.Request(url, headers=headers, method="GET") try: response = urllib.request.build_opener(NoRedirect).open(request, timeout=30) except urllib.error.HTTPError as error: response = error print(response.status, response.read().decode("utf-8", errors="replace")) # HTTP-Status und dokumentierte error/success-Felder gemeinsam prüfen. ``` ## Vollständige Operationsdefinition und edoobox-Hinweise Alle Originalfelder einschliesslich `x-edoobox-*`, requestBody und responses bleiben erhalten. Teilweise dokumentierte Schemata sind kein vollständiger Validierungsvertrag. ```json {"operationId":"get_v2_widget_list_LUT","summary":"Verfügbare Widgettypen für eine Seitenvorlage lesen","description":"Liefert die im Konto für template verfügbaren Widgettypen mit Namen, Struktur, Layoutvorgaben und sichtbaren Einstellungen. Der separate Queryparameter template ist erforderlich. Die Antwort dient zur Wahl von key und zur Validierung von config.data bei POST/PUT widget; angebotene Typen hängen von Vorlage und Kontofunktionen ab.","tags":["widget"],"security":[{"bearerAuth":[],"accountContext":[]},{"sessionCookie":[],"accountContext":[]}],"parameters":[{"name":"template","in":"query","required":true,"description":"Seitenvorlagenschlüssel aus /design/list/templates.","schema":{"type":"string","enum":["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s"],"examples":["a"]}},{"name":"language","in":"header","required":false,"description":"Account language code, where the resource supports setLanguage().","schema":{"type":"string","examples":["de"]}}],"responses":{"200":{"description":"data.template und ein nach Widget-key indiziertes data.widget mit aktuellen Widgetdefinitionen.","content":{"application/json":{"example":{"success":true,"error":false,"data":{"template":"a","widget":{}}},"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":["boolean","string"]},"error_code":{"type":"string"},"data":{"type":"object","properties":{"template":{"type":"string"},"widget":{"type":"object","additionalProperties":{"type":"object","additionalProperties":true,"description":"Widgettyp mit dynamischer structure und Layoutvorgaben; Schlüssel ist der für POST erforderliche Widget-key."}}},"additionalProperties":true,"required":["template","widget"]},"type":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"example":{"success":false,"error":"ED40006"}}}},"401":{"description":"Missing/expired token, missing account context, account paused or model-specific authorization failure.","content":{"application/json":{"example":{"success":false,"error":"ED40103"}}}},"402":{"description":"Missing/invalid action data or subscription/operation restriction.","content":{"application/json":{"example":{"success":false,"error":"ED80100"}}}},"403":{"description":"Permission denied. Browser CORS rejection may have an empty body.","content":{"application/json":{"example":{"success":false,"error":"ED40340"}}}},"404":{"description":"Unknown resource/action or unsupported method.","content":{"application/json":{"example":{"success":false,"error":"ED80019"}}}},"422":{"description":"Validation failed.","content":{"application/json":{"example":{"success":false,"error":"ED80020"}}}},"429":{"description":"Request/login rate limit exceeded. Honor Retry-After when supplied; legacy rate-limit paths may omit it. Retry only proven reads within a bounded deadline; a mutation is never automatically replayed after an ambiguous response.","content":{"application/json":{"example":{"success":false,"error":"ED42902"}}},"headers":{"Retry-After":{"description":"Optional server delay in whole seconds or an HTTP date. Absence is not permission for an immediate retry.","schema":{"type":"string","examples":["30"]}}}},"502":{"description":"Server-side operation failed.","content":{"application/json":{"example":{"success":false,"error":"ED50201"}}}}},"x-edoobox-mutates-state":false,"x-edoobox-mutationReview":{"source":{"file":"application/models/v2/Widgetfunc.php","line":50},"reason":"Lookup/configuration catalog reads; template construction uses existing rows only."},"x-edoobox-runtimeMutationGuard":false,"x-edoobox-get-effect":"read","x-edoobox-documentationReview":{"file":"docs/openapi/operation-contracts/07-layout-files.json","notes":[]},"x-edoobox-source":{"file":"application/models/v2/Widget.php","method":"getWidget","line":213,"branch":{"file":"application/models/v2/Widget.php","line":336}},"x-edoobox-documentationStatus":"reviewed","x-edoobox-readOnly":true,"x-edoobox-safeForAutomation":true,"x-edoobox-permission":{"resource":"widget","short":"wigt","checkedByDispatcher":true,"levels":[1,3,4],"description":"Resource permission mapping from APIPermissions(), Authfunc::_getCheckJWT. Additional model/admin/plugin checks may apply; resources absent from api_permission do not acquire a fictitious scope."}} ```